Druid has no query sessions, so the panel shows tasks
Rows come from the task table and every column is relabelled to say so, because there is no session catalog and no query log anywhere to read.
Open the monitoring dashboard on an Apache Druid connection and the Sessions tab
lists rows. None of them is a session. Druid monitoring is sys tables and
INFORMATION_SCHEMA reads over the one SQL endpoint, and among those tables
there is no sys.queries and no connection catalog of any kind. What the panel
lists is ingestion tasks, and every column in the row has been relabelled to say
so out loud.
A panel here takes one of three shapes: it reads a system table, or it reads one and calls the result something other than the panel’s own name, or it sends no statement at all because the fact it wants is not in SQL anywhere. All three are in the dashboard at once.
Which sys tables each Druid monitoring panel reads
The provider has no driver. Every statement, including every monitoring read, is
a JSON body posted to POST /druid/v2/sql on port 8888 (the Router) or 8082
(the Broker) with the runtime’s own fetch. So a monitoring panel here is a
SELECT and nothing more exotic:
| Panel | Statement source | What the number is |
|---|---|---|
| Overview identity, uptime | sys.servers |
version and start time from the Coordinator’s row, Broker as fallback |
| Overview size | sys.segments |
SUM(size) over active segments |
| Overview datasource count | INFORMATION_SCHEMA.TABLES |
rows where TABLE_SCHEMA = 'druid' |
| Overview active connections | sys.tasks |
the count of RUNNING tasks |
| Tables | sys.segments where is_active = 1 |
rows and bytes grouped by datasource |
| Storage | sys.servers where server_type = 'historical' |
curr_size over max_size per historical |
| Sessions | sys.tasks where status IN ('RUNNING','PENDING') |
ingestion tasks |
| Performance | none | zeroed |
| Slow queries | none | empty |
| Indexes | none | empty |
The is_active = 1 filter on every sys.segments read is not a speed
optimisation. That table describes every segment the metadata store knows about,
including segments superseded by a compaction or by re-ingesting the same
interval. Summing all of them counts the same rows and bytes twice, so a
re-ingested datasource would appear to have doubled in size.
The storage division has the same character. sys.servers reports max_size = 0
for every process that is not a historical, so on an ordinary cluster the usage
percentage meets a zero denominator. It yields 0 there, not a flattering 100
and not NaN.
Four separate reads, so one denial is not fatal
The overview needs four facts and could ask for them in one statement. It does
not. sys.servers, sys.segments, INFORMATION_SCHEMA.TABLES and sys.tasks
are four separate round trips.
The reason is the permission model. On a cluster running
druid-basic-security, access to the sys schema is granted table by table. A
role that is allowed the datasource count but declined sys.tasks would, under
one joined statement, lose the whole overview to a single authorization failure.
Split, it loses one card.
Only three error categories are allowed to degrade a panel to empty:
UNAUTHORIZED, FORBIDDEN and NOT_FOUND. Anything else keeps propagating and
becomes a message the user reads. That boundary matters more than it sounds,
because an empty monitoring panel is the perfect hiding place for a mistake the
user made themselves.
Failures are classified by the category Druid reports, never by the HTTP
status, because the status misclassifies in both directions. SELECT 1/0 - an
ordinary typo - answers HTTP 500 with persona: "ADMIN" and
category: UNCATEGORIZED. Reading 5xx as “the cluster is broken” would send
someone to check their host over a division by zero.
The schema tree goes further and touches no sys table at all: datasources and
columns come from INFORMATION_SCHEMA only. A cluster that declines to describe
its servers still renders a full sidebar.
Relabelling a session panel that lists tasks
Returning an empty Sessions tab would have been defensible and would have been wrong. A multi-hour ingestion saturating the MiddleManagers is real activity, and a blank panel would report a quiet cluster. Tasks are the only activity Druid can describe, so they are what the panel shows - and each row is made self-describing rather than dressed up as a connection:
| Session field | Druid value |
|---|---|
applicationName |
the constant Druid ingestion task |
pid |
task_id |
database |
datasource, or the literal none for a task such as noop |
state |
RUNNING or PENDING |
query |
the task type: index_parallel, compact, kill |
user |
unknown |
durationMs |
CURRENT_TIMESTAMP - created_time |
Two of those are deliberate refusals. user is unknown because sys.tasks
records no submitter identity; borrowing the connection’s own user would credit
that account with a task it did not submit. And the age is computed from two
readings of the server’s clock rather than from sys.tasks.duration, because
that column is -1 for a task that has not finished, which is every task this
query selects. Projecting it would print -1ms on every row, so it is not
projected at all.
The applicationName constant is the load-bearing part. It is what stops a
reader skimming the tab from concluding that Druid has a connection catalog after
all.
Metrics that reach an emitter, never a table
The Performance tab is zeroed and sends no statement, because Druid’s metrics reach an emitter rather than a readable table, and the slow-query list is permanently empty because no system table and no endpoint holds finished queries.
Druid’s cache, query and ingestion metrics are emitted - to statsd, to Kafka, to
an HTTP endpoint, to the log. None of them lands anywhere SQL can read, and no
configuration setting moves them into sys.
Two consequences follow. cacheHitRatio in the health summary is
the string N/A, not a number: that field is typed as a string, so it can say
not measured, and a fabricated low number would trip the cache-ratio threshold
alert into reporting a fault that does not exist. Inside the performance metrics
themselves the same ratio is a required field, so it carries a neutral 0 while
every other metric in that type is optional and is left out entirely - a zero
there would read as a measurement of zero, which is a different and false claim.
The second consequence is maxConnections, which is 0 because Druid publishes
no connection limit anywhere in SQL. It has no pool - each statement is one
stateless HTTP request.
Panels that send no statement at all
Three more panels send no statement either, and the silence is the design.
Slow queries. Druid keeps no query log: no system table, no endpoint, no file. The empty state says exactly that, in place of the rows. The panel does not send a probe to discover it, because there is nothing to probe.
Indexes. Druid indexes every dimension inside its segment, but those indexes
have no name, no size and no usage counter. There is no index object a row could
describe, so indexCount is 0 and the list is empty.
Maintenance. Nothing in the maintenance vocabulary - vacuum, analyze,
reindex, optimize, check, kill - has a SQL-reachable Druid analogue. Compaction
and retention are Coordinator and task concerns. kill is impossible for a
second, independent reason: with no sys.queries there is nowhere honest to read
a cancellable query id from. So the controls do not render, and the Vacuum
summary card reads N/A rather than counting zero bloated tables and awarding a
green OK for an operation that cannot run.
The distinction the dashboard keeps is between absent and empty. An absent panel means the engine could not answer; an empty panel means zero is a real measurement. Druid holds no finished queries and no index objects, so zero is the true count and an empty list states it correctly. A denied panel is a different claim, and the two are not allowed to look alike.
None of this is unique to Druid in kind, only in which panels it hits. The monitoring feature page states the same boundary in general terms - what a panel can show is bounded by what the engine reports - and the engine pages carry each engine’s line next to its transport and default port.