|
| 1 | +-- PostgreSQL-flavoured SQL |
| 2 | + |
| 3 | +CREATE TABLE metrics(labels jsonb, value float8); |
| 4 | + |
| 5 | +INSERT INTO metrics(labels, value) |
| 6 | +VALUES |
| 7 | +('{"__name__": "requests_total", "instance": "foo", "endpoint": "/api/v1/status"}', 1000), |
| 8 | +('{"__name__": "requests_total", "instance": "foo", "endpoint": "/api/v1/query"}', 2000), |
| 9 | +('{"__name__": "requests_total", "instance": "bar", "endpoint": "/api/v1/status"}', 2500), |
| 10 | +('{"__name__": "requests_total", "instance": "ownermissing", "endpoint": "/api/v1/status"}', 3000); |
| 11 | + |
| 12 | +INSERT INTO metrics(labels, value) |
| 13 | +VALUES |
| 14 | +('{"__name__": "component_price_multiplier", "instance": "foo", "owner": "internaluser1"}', 1), |
| 15 | +('{"__name__": "component_price_multiplier", "instance": "bar", "owner": "ushealthcare"}', 20), |
| 16 | +('{"__name__": "component_price_multiplier", "instance":"baz", "owner":"idleandunused"}', 0); |
| 17 | + |
| 18 | +-- For convenient reference to specific metric series |
| 19 | +CREATE VIEW requests_total AS |
| 20 | +SELECT * FROM metrics WHERE labels ->> '__name__' = 'requests_total'; |
| 21 | + |
| 22 | +CREATE VIEW component_price_multiplier AS |
| 23 | +SELECT * FROM metrics WHERE labels ->> '__name__' = 'component_price_multiplier'; |
| 24 | + |
| 25 | +-- Like: |
| 26 | +-- |
| 27 | +-- requests_total{} |
| 28 | +-- * on (instance) |
| 29 | +-- group_left(owner) |
| 30 | +-- component_price_multiplier{} |
| 31 | + |
| 32 | +SELECT |
| 33 | + jsonb_insert( |
| 34 | + -- take all LHS labels |
| 35 | + requests_total.labels, |
| 36 | + -- add owner label from RHS |
| 37 | + ARRAY['owner'], component_price_multiplier.labels -> 'owner' |
| 38 | + ) AS labels, |
| 39 | + -- apply arithmetic on value |
| 40 | + requests_total.value * component_price_multiplier.value AS value |
| 41 | +FROM requests_total |
| 42 | +-- keep rows for which a LHS and RHS match exists |
| 43 | +INNER JOIN component_price_multiplier ON ( |
| 44 | + requests_total.labels ->> 'instance' = component_price_multiplier.labels ->> 'instance' |
| 45 | +); |
| 46 | + |
| 47 | +-- Result: |
| 48 | +-- labels | value |
| 49 | +-- -----------------------------------------------------------------------------------------------------------+------- |
| 50 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "foo"} | 1000 |
| 51 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/query", "instance": "foo"} | 2000 |
| 52 | +-- {"owner": "ushealthcare", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "bar"} | 50000 |
| 53 | +-- (3 rows) |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +-- Now as a left join so we keep the unmatched LHS. Note the coalesce(...) for the value, because |
| 58 | +-- lhs.value * rhs.value is null if the rhs is null in a left join. |
| 59 | +SELECT |
| 60 | + coalesce( |
| 61 | + -- if RHS matched, we can return a result with the owner label |
| 62 | + jsonb_insert( |
| 63 | + -- take all LHS labels |
| 64 | + requests_total.labels, |
| 65 | + -- add owner label from RHS |
| 66 | + ARRAY['owner'], component_price_multiplier.labels -> 'owner' |
| 67 | + ), |
| 68 | + -- otherwise, we return the LHS labels unchanged |
| 69 | + requests_total.labels |
| 70 | + ) AS labels, |
| 71 | + coalesce( |
| 72 | + -- if lhs and rhs did not match the rhs here is null, so |
| 73 | + requests_total.value * component_price_multiplier.value, |
| 74 | + -- pick the lhs if so |
| 75 | + requests_total.value |
| 76 | + ) AS value |
| 77 | +FROM requests_total |
| 78 | +-- keep all LHS rows, add RHS fields only where LHS and RHS match, otherwise |
| 79 | +-- RHS fields will be null |
| 80 | +LEFT JOIN component_price_multiplier ON ( |
| 81 | + requests_total.labels ->> 'instance' = component_price_multiplier.labels ->> 'instance' |
| 82 | +); |
| 83 | + |
| 84 | +-- Result: |
| 85 | +-- |
| 86 | +-- labels | value |
| 87 | +-- -----------------------------------------------------------------------------------------------------------+------- |
| 88 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "foo"} | 1000 |
| 89 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/query", "instance": "foo"} | 2000 |
| 90 | +-- {"owner": "ushealthcare", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "bar"} | 50000 |
| 91 | +-- {"__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "ownermissing"} | 3000 |
| 92 | +-- (4 rows) |
| 93 | + |
| 94 | + |
| 95 | +-- Add the extra component_price_multiplier{instance="foo"} row with extra subcategory="discount" label |
| 96 | +INSERT INTO metrics(labels, value) |
| 97 | +VALUES |
| 98 | +('{"__name__": "component_price_multiplier", "instance": "foo", "subcategory": "discount", "owner": "internaluser1"}', 0.9); |
| 99 | + |
| 100 | + |
| 101 | +-- and repeat the first query |
| 102 | + |
| 103 | +SELECT |
| 104 | + -- take all LHS labels, add 'owner' label from RHS |
| 105 | + jsonb_insert(requests_total.labels, ARRAY['owner'], component_price_multiplier.labels -> 'owner') AS labels, |
| 106 | + -- apply arithmetic on value |
| 107 | + requests_total.value * component_price_multiplier.value AS value |
| 108 | +FROM requests_total |
| 109 | +-- keep rows for which a LHS and RHS match exists |
| 110 | +INNER JOIN component_price_multiplier ON ( |
| 111 | + requests_total.labels ->> 'instance' = component_price_multiplier.labels ->> 'instance' |
| 112 | +); |
| 113 | + |
| 114 | +-- result: duplicate series (which would not be allowed in PromQL, resulting in an error) |
| 115 | +-- |
| 116 | +-- labels | value |
| 117 | +-- -----------------------------------------------------------------------------------------------------------+------- |
| 118 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "foo"} | 900 |
| 119 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "foo"} | 1000 |
| 120 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/query", "instance": "foo"} | 1800 |
| 121 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/query", "instance": "foo"} | 2000 |
| 122 | +-- {"owner": "ushealthcare", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "bar"} | 50000 |
| 123 | +-- (5 rows) |
| 124 | + |
| 125 | +-- We can correct this by adding subcategory to the join conditions |
| 126 | +SELECT |
| 127 | + -- take all LHS labels, add 'owner' label from RHS |
| 128 | + jsonb_insert(requests_total.labels, ARRAY['owner'], component_price_multiplier.labels -> 'owner') AS labels, |
| 129 | + -- apply arithmetic on value |
| 130 | + requests_total.value * component_price_multiplier.value AS value |
| 131 | +FROM requests_total |
| 132 | +-- keep rows for which a LHS and RHS match exists |
| 133 | +INNER JOIN component_price_multiplier ON ( |
| 134 | + requests_total.labels ->> 'instance' = component_price_multiplier.labels ->> 'instance' |
| 135 | + AND |
| 136 | + -- Here we need to treat NULL = 'non-null' as false, not null, hence IS DISTINCT FROM instead of = |
| 137 | + requests_total.labels ->> 'subcategory' IS NOT DISTINCT FROM component_price_multiplier.labels ->> 'subcategory' |
| 138 | +); |
| 139 | + |
| 140 | +-- result: |
| 141 | +-- labels | value |
| 142 | +-- -----------------------------------------------------------------------------------------------------------+------- |
| 143 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "foo"} | 1000 |
| 144 | +-- {"owner": "internaluser1", "__name__": "requests_total", "endpoint": "/api/v1/query", "instance": "foo"} | 2000 |
| 145 | +-- {"owner": "ushealthcare", "__name__": "requests_total", "endpoint": "/api/v1/status", "instance": "bar"} | 50000 |
| 146 | +-- (3 rows) |
| 147 | + |
| 148 | + |
0 commit comments