-
Notifications
You must be signed in to change notification settings - Fork 59
Description
Here's a query that should be fast, but isn't:
get switch_data_link:pcs_invalid_errors | filter timestamp > @now() - 15m | last 1
We're looking up the latest value of each series for the switch_data_link:pcs_invalid_errors metric. There are <100 series on dogfood, and we only want one sample per series, so how long could it possibly take?
joshcarp@j0sh ~/c/o/oximeter (jmcarp/clickhouse-part-log)> time oxide --profile r2 api -X POST /v1/system/timeseries/query --field query='get switch_data_link:pcs_invalid_errors | filter timestamp > @now() - 15m | last 1' > /dev/null
________________________________________________________
Executed in 2.58 secs fish external
usr time 20.00 millis 0.27 millis 19.73 millis
sys time 23.23 millis 2.42 millis 20.81 millis
Let's figure out which clickhouse queries are taking time here:
joshcarp@j0sh ~/c/o/oximeter (jmcarp/clickhouse-part-log)> time oxide --profile r2 api -X POST /v1/system/timeseries/query --field query='get switch_data_link:pcs_invalid_errors | filter timestamp > @now() - 15m | last 1' --field include_summaries=true | jq '.query_summaries'
[
{
"elapsed_ms": 1304,
"id": "4a2eb72e-86e5-4d23-b499-ee7a8046f803",
"io_summary": {
"read": {
"bytes": 26322808,
"rows": 373001
},
"written": {
"bytes": 0,
"rows": 0
}
},
"query": "SELECT filter_on_asic_fab.timeseries_key AS timeseries_key, filter_on_asic_fab.field_value AS asic_fab, filter_on_asic_lot.field_value AS asic_lot, filter_on_asic_wafer.field_value AS asic_wafer, filter_on_asic_wafer_loc_x.field_value AS asic_wafer_loc_x, filter_on_asic_wafer_loc_y.field_value AS asic_wafer_loc_y, filter_on_kind.field_value AS kind, filter_on_link_id.field_value AS link_id, filter_on_model.field_value AS model, filter_on_network.field_value AS network, filter_on_port_id.field_value AS port_id, filter_on_rack_id.field_value AS rack_id, filter_on_sled_id.field_value AS sled_id, filter_on_sled_model.field_value AS sled_model, filter_on_sled_revision.field_value AS sled_revision, filter_on_sled_serial.field_value AS sled_serial, filter_on_switch_id.field_value AS switch_id, filter_on_switch_model.field_value AS switch_model, filter_on_switch_revision.field_value AS switch_revision, filter_on_switch_serial.field_value AS switch_serial, filter_on_switch_slot.field_value AS switch_slot FROM (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'asic_fab' ) AS filter_on_asic_fab INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'asic_lot' ) AS filter_on_asic_lot ON filter_on_asic_lot.timeseries_key = filter_on_asic_fab.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_u8 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'asic_wafer' ) AS filter_on_asic_wafer ON filter_on_asic_wafer.timeseries_key = filter_on_asic_lot.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_i16 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'asic_wafer_loc_x' ) AS filter_on_asic_wafer_loc_x ON filter_on_asic_wafer_loc_x.timeseries_key = filter_on_asic_wafer.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_i16 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'asic_wafer_loc_y' ) AS filter_on_asic_wafer_loc_y ON filter_on_asic_wafer_loc_y.timeseries_key = filter_on_asic_wafer_loc_x.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'kind' ) AS filter_on_kind ON filter_on_kind.timeseries_key = filter_on_asic_wafer_loc_y.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_u8 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'link_id' ) AS filter_on_link_id ON filter_on_link_id.timeseries_key = filter_on_kind.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'model' ) AS filter_on_model ON filter_on_model.timeseries_key = filter_on_link_id.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'network' ) AS filter_on_network ON filter_on_network.timeseries_key = filter_on_model.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'port_id' ) AS filter_on_port_id ON filter_on_port_id.timeseries_key = filter_on_network.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_uuid WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'rack_id' ) AS filter_on_rack_id ON filter_on_rack_id.timeseries_key = filter_on_port_id.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_uuid WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'sled_id' ) AS filter_on_sled_id ON filter_on_sled_id.timeseries_key = filter_on_rack_id.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'sled_model' ) AS filter_on_sled_model ON filter_on_sled_model.timeseries_key = filter_on_sled_id.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_u32 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'sled_revision' ) AS filter_on_sled_revision ON filter_on_sled_revision.timeseries_key = filter_on_sled_model.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'sled_serial' ) AS filter_on_sled_serial ON filter_on_sled_serial.timeseries_key = filter_on_sled_revision.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_uuid WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'switch_id' ) AS filter_on_switch_id ON filter_on_switch_id.timeseries_key = filter_on_sled_serial.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'switch_model' ) AS filter_on_switch_model ON filter_on_switch_model.timeseries_key = filter_on_switch_id.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_u32 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'switch_revision' ) AS filter_on_switch_revision ON filter_on_switch_revision.timeseries_key = filter_on_switch_model.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_string WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'switch_serial' ) AS filter_on_switch_serial ON filter_on_switch_serial.timeseries_key = filter_on_switch_revision.timeseries_key INNER JOIN (SELECT DISTINCT timeseries_key, field_value FROM oximeter.fields_u16 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND field_name = 'switch_slot' ) AS filter_on_switch_slot ON filter_on_switch_slot.timeseries_key = filter_on_switch_serial.timeseries_key"
},
{
"elapsed_ms": 27,
"id": "f87045b5-6d8d-46f6-b869-c28546c9683f",
"io_summary": {
"read": {
"bytes": 38452785,
"rows": 4734976
},
"written": {
"bytes": 0,
"rows": 0
}
},
"query": "WITH another_sort_bites_the_dust AS (SELECT timeseries_key, start_time, timestamp, datum FROM oximeter.measurements_cumulativeu64 WHERE timeseries_name = 'switch_data_link:pcs_invalid_errors' AND ((greater(timestamp, '2025-10-18 02:23:13.568113428') AND timeseries_key IN (756097590731240185,1103678389185180213,1114318808045492183,2778933048436799581,2854864349868441750,2880055957681953789,2988357157493497291,3431400205349850520,4197624739596116631,4482317240533975040,4523730516240309666,4546337385136722914,4730845005745019941,5051034547903499618,5214763097398691070,5531446474934082220,5914094962255628952,6296741838962653061,6500850491531141289,6547455214936556723,6801096096785608471,6840059004777660900,6956768772222382044,7845955728345151377,8079285099772927279,8371638504720300988,8685222285163140992,8895081521639637420,9021094672032217726,9076816697852073085,9183890932061987237,9847120510806684081,9989711210244049645,10002743743499159209,10023093142870887259,10043270192420610430,10444555488425996914,10773026991683650248,10905986825036860523,11007200161122304200,11394909356247832469,12464387133381661438,12543560225616984746,12744659831664932266,12911136620986716246,12958578737857061770,13254999271480575466,13627503505782314272,13652221451308349941,13678933375858933474,13683736416652084102,13846398855017622740,14059609743711840178,14385675600390572247,15070769942095295940,15435437835838139192,15493658260864888608,15563854730682873276,15788168535646315722,15834530069215973581,16123362320570486376,16146958724618824409,16510965828585083258,16652066512748240246,16666885187928053436,16941864685879894581,17138776637522959523,17625682851552006998,17820117293304220522,17975557493162096426))) ORDER BY timeseries_key, timestamp DESC LIMIT 1 BY timeseries_key) SELECT * FROM another_sort_bites_the_dust ORDER BY timeseries_key, timestamp LIMIT 1000001"
}
]
We're running two queries: one to look up field labels and values for the relevant timeseries in the fields_* tables, and a second to look up the metric values in measurements_cumulativeu64. For this particular run, the former query took 1304ms, and the latter took 27ms. We're spending a lot more time looking up labels than data points! And there aren't many unique timeseries or labels, so intuitively, we should be able to do much better.