Job statistics collector¶
Collect query statistics from sys.jobs_log continuously, and keep them beyond the
retention of that table. collect and view handle the raw statistics, report and ui
launch an interpreted, interactive dashboard on top of the same collected data.
This is distinct from the one-shot ctk info jobs snapshot. For a side-by-side comparison
of both, see Comparison with ctk cfr jobstats.
Install¶
pip install --upgrade 'cratedb-toolkit[cfr]'
Tip
Alternatively, use the Docker image per ghcr.io/crate/cratedb-toolkit.
For more information about installing CrateDB Toolkit, see Installation.
Synopsis¶
The collector stores its statistics in the schema of the cluster URL, stats by default.
export CRATEDB_CLUSTER_URL=crate://crate@localhost:4200/?schema=stats
Collects statistics on an ongoing basis:
ctk cfr jobstats collect
Prints collected statistics as a JSON document:
ctk cfr jobstats view
Shows the top 10 collected statements, sorted by runtime descending:
ctk cfr jobstats report
Launches a web interface with visualisations for interactive exploration of statistics:
ctk cfr jobstats ui
Note
Please collect statistics first using ctk cfr jobstats collect, then use the other
commands to display or explore them. view creates its tables on demand, so an empty
result means nothing has been collected into that schema yet.
How collection works¶
collect polls sys.jobs_log for jobs which finished since the last poll, and folds them
into per-statement statistics. Statements against sys.* and information_schema.* are
skipped, which excludes the collector’s own poll query, but not its writes.
Note
The collector observes itself. Reading sys.jobs_log is filtered out, however the
statements which store the statistics are regular DDL and DML against the tables below,
so the next cycle collects them like any other query. On a quiet cluster, the statistics
therefore consist mostly of the collector’s own CREATE TABLE IF NOT EXISTS,
REFRESH TABLE, SELECT, INSERT, and UPDATE statements. Take that into account when
interpreting calls. Pointing --reportdb at a separate cluster avoids it, because the
statistics are then written outside the cluster being observed. A --reportdb which only
differs in the schema does not help, as those writes still show up in sys.jobs_log.
How far the collector has come is recorded as a watermark, so a restarted collector picks up where it left off, instead of counting the same jobs again. Each cycle considers jobs which ended after the watermark, up to and including the current moment.
Per distinct statement, the collector maintains:
calls— how often the statement has been executedbucket— a histogram of the execution durations, in milliseconds. A duration is counted into the first bucket whose threshold it stays below. The thresholds are 10, 50, 100, 500, 1000, 2000, 5000, 10000, 15000, and 20000, plusINFfor everything slower.avg_duration— a decaying average, updated as(previous + current) / 2per execution. Recent executions therefore weigh much more heavily than an arithmetic mean over all executions would.nodes— the nodes which have run the statement, without duplicateslast_used— when the most recent execution of the statement startedusername,query_type— as reported by CrateDB, taken from the execution which introduced the statement. Neither is updated afterwards, so when the same statement is run by several users, the record keeps the user which ran it first and does not grow a second record for the others. Usectk info jobsfor a per-user breakdown.
Tables¶
Two tables are created in the configured schema.
"<schema>".jobstats_statements holds one record per distinct statement:
Column |
Type |
Description |
|---|---|---|
|
|
identifier assigned by the collector |
|
|
the statement, anonymized when collected with |
|
|
number of executions counted so far |
|
|
duration histogram, keyed by threshold |
|
|
when the statement was last seen |
|
|
user which ran the statement |
|
|
statement classification reported by CrateDB |
|
|
decaying average duration, in milliseconds |
|
|
nodes which have run the statement, as JSON strings of the |
"<schema>".jobstats_last holds a single record, the watermark:
Column |
Type |
Description |
|---|---|---|
|
|
up to when jobs have been collected |
Configuration¶
The cluster address is taken from --cluster-url / CRATEDB_CLUSTER_URL, and the schema
from its ?schema= parameter, defaulting to stats. Additionally, these environment
variables are recognized.
INTERVAL— how long to sleep between two collection cycles, in seconds. Default:10.INITIAL_LOOKBACK_SECONDS— how far back to look for jobs when no watermark has been recorded yet, in seconds. Default:600.STMT_TABLE— full-qualified name of the statistics table, overriding the default"<schema>".jobstats_statements.LAST_EXEC_TABLE— full-qualified name of the watermark table, overriding the default"<schema>".jobstats_last.
Options
ctk cfr jobstats collect:
--once— record only one sample, then exit, instead of collecting continuously--reportdb/-r— a separate database URL to store the statistics in (crate://crate@localhost:4200/?schema=stats&sslmode=require). Jobs are read from the cluster URL, and written to this one.--anonymize— path to a decoder dictionary file for anonymizing SQL statements before they are stored; using the flag without a value defaults todecoder_dictionary.jsonin the current working directory. The file does not need to exist: it is created on the first run, and extended as further identifiers are encountered.
ctk cfr jobstats view:
--reportdb/-r— a separate database URL to read the statistics from--deanonymize— path to the decoder dictionary file used to reverse--anonymize, to view statements in their original form
ctk cfr jobstats report and ctk cfr jobstats ui read the statistics from the schema of
the cluster URL. They do not accept --reportdb, so when collect --reportdb was used,
address that database per --cluster-url here. ui serves the dashboard on
localhost:7777.
Anonymization¶
With --anonymize, statements are anonymized before they are stored, and the substitutions
are recorded in the decoder dictionary file. The file is created on the first run and grows
as new identifiers are encountered. Keep it: it is the only way to make the collected
statements legible again, using view --deanonymize.
ctk cfr jobstats collect --once --anonymize ./decoder_dictionary.json
ctk cfr jobstats view --deanonymize ./decoder_dictionary.json
Warning
The decoder dictionary maps anonymized tokens back to the original identifiers and string literals. Treat it as confidential, do not ship it together with the collected statistics.
Note
Anonymization fails closed: when a statement cannot be anonymized, it is stored as
<redacted: <digest>> rather than in clear text. Statistics per distinct statement remain
meaningful, but such statements cannot be recovered with --deanonymize. The event is
reported as a warning.
Expect this to happen occasionally, and more often as the dictionary grows: the underlying
queryanonymizer can fail with a PatternError on a statement it anonymized successfully
when the dictionary was still small.