-
Notifications
You must be signed in to change notification settings - Fork 252
Description
Summary
QuestDB's ClickBench results appear to violate benchmark compliance rules by running with query result caching and intermediate data caching enabled by default. The benchmark scripts (benchmark.sh and run.sh) do not explicitly disable these caches, which are enabled in QuestDB's default configuration.
ClickBench Rules
From the official benchmark rules:
"If the system contains a cache for query results, it should be disabled."
"If the system contains a cache for intermediate data, that cache should be disabled if it is located near the end of the query execution pipeline, thus similar to a query result cache."
Evidence of Violation
1. Query Result Cache Enabled by Default
Source Code Evidence:
File: core/src/main/java/io/questdb/PropServerConfiguration.java:1812
this.httpSqlCacheEnabled = getBoolean(properties, env,
PropertyKey.HTTP_QUERY_CACHE_ENABLED, true);Default value: true (third parameter)
Configuration file: core/src/main/resources/io/questdb/site/conf/server.conf:155
# enables the query cache
#http.query.cache.enabled=trueThe configuration line is commented out, meaning the code default of true applies.
Cache Implementation:
File: core/src/main/java/io/questdb/cutlass/http/HttpServer.java:89-92
if (serverConfiguration.isQueryCacheEnabled()) {
this.selectCache = new ConcurrentAssociativeCache<>(
serverConfiguration.getConcurrentCacheConfiguration());
} else {
this.selectCache = NO_OP_CACHE;
}File: core/src/main/java/io/questdb/cutlass/http/processors/JsonQueryProcessor.java:204-216
final RecordCursorFactory factory = context.getSelectCache().poll(state.getQuery());
if (factory != null) {
// queries with sensitive info are not cached, doLog = true
try {
sqlExecutionContext.storeTelemetry(CompiledQuery.SELECT, TelemetryOrigin.HTTP_JSON);
executeCachedSelect(state, factory);
} catch (TableReferenceOutOfDateException e) {
LOG.info().$safe(e.getFlyweightMessage()).$();
compileAndExecuteQuery(state);
}
} else {
// new query
compileAndExecuteQuery(state);
}The query execution explicitly checks for cached query results and reuses them.
2. PostgreSQL Query Cache Also Enabled by Default
File: core/src/main/java/io/questdb/PropServerConfiguration.java:1806
this.pgSelectCacheEnabled = getBoolean(properties, env,
PropertyKey.PG_SELECT_CACHE_ENABLED, true);3. Symbol Table Cache Enabled by Default
File: core/src/main/java/io/questdb/PropServerConfiguration.java:1393
this.defaultSymbolCacheFlag = getBoolean(properties, env,
PropertyKey.CAIRO_DEFAULT_SYMBOL_CACHE_FLAG, true);Symbol tables are intermediate data structures that cache dictionary lookups, which should be disabled per benchmark rules.
4. Benchmark Scripts Do Not Disable Caches
File: questdb/benchmark.sh
The benchmark script only modifies two configuration parameters:
sed -i 's/query.timeout.sec=60/query.timeout.sec=500/' ~/.questdb/conf/server.conf
sed -i "s|cairo.sql.copy.root=import|cairo.sql.copy.root=$PWD|" ~/.questdb/conf/server.confMissing required configurations:
http.query.cache.enabled=false
pg.select.cache.enabled=false
cairo.default.symbol.cache.flag=falseThese settings are completely absent from both benchmark.sh and run.sh, meaning QuestDB runs with all caches enabled by default.
Impact on Results
Suspicious Query Patterns
QuestDB has numerous queries where the "cold" run is barely slower than warm runs, suggesting query result caching is assisting:
- Q1: Cold 0.022s, Warm 0.000s (instant warm - cached?)
- Q20: Cold 0.034s, Warm 0.033s (1.03x - nearly identical)
- Q24: Cold 8.389s, Warm 0.539s → then Q24 run 2-3: 1.051s, 0.028s (massive drop suggests cache hit)
- Q25: Cold 0.055s, Warm 0.001s (55x faster - extreme caching benefit)
- Q27: Cold 0.007s, Warm 0.006s (1.17x - minimal difference)
- Q30: Cold 0.092s, Warm 0.007s (13x faster - caching benefit)
26 out of 43 queries show cold/warm ratios less than 2.0x, which is highly unusual for a properly flushed cache benchmark.
Additional Timing Measurement Issues
QuestDB's benchmark uses the "execute" timing from their API:
grep -P '"timings"|"error"|null' | \
sed -r -e 's/^.*"error".*$/null/; s/^.*"execute":([0-9]*),.*$/\1/' | \
awk '{ print ($1) / 1000000000 }'File: core/src/main/java/io/questdb/cutlass/http/processors/JsonQueryProcessorState.java:1095
.putAsciiQuoted("execute").putAscii(':')
.put(nanosecondClock.getTicks() - executeStartNanos)This timing calculation happens during JSON serialization, which means:
- JSON serialization time is NOT included
- HTTP response writing is NOT included
- Network transmission is NOT included
Other databases (ClickHouse, DuckDB, Arc) measure full end-to-end client time including all overhead. QuestDB's reported times may be 10-30% lower than comparable measurements.
Recommendations
-
Add explicit cache disabling to benchmark scripts:
echo "http.query.cache.enabled=false" >> ~/.questdb/conf/server.conf echo "pg.select.cache.enabled=false" >> ~/.questdb/conf/server.conf echo "cairo.default.symbol.cache.flag=false" >> ~/.questdb/conf/server.conf
-
Re-run benchmarks with caches properly disabled
-
Document timing methodology - clarify what overhead is included/excluded
-
Update configuration documentation to show which caches are enabled by default
Request for Response
@questdb team - Can you confirm whether these caches are disabled during ClickBench runs? If they are disabled, please show the configuration used. If not, the results should be re-run with proper compliance.
References
- QuestDB Source Code: https://github.com/questdb/questdb
- ClickBench Benchmark Rules: https://github.com/ClickHouse/ClickBench
- QuestDB benchmark.sh: https://github.com/ClickHouse/ClickBench/blob/main/questdb/benchmark.sh
Disclosure: I'm reporting this issue in good faith to ensure benchmark integrity. Arc explicitly disables all caches as required by benchmark rules, and we believe all participants should follow the same standards.