From 96afa7adeb2914284af0e93eec3793145e896ba3 Mon Sep 17 00:00:00 2001 From: Viacheslav Mitrofanov Date: Sun, 22 Oct 2023 11:33:34 +0100 Subject: [PATCH] Raw query tip and README.md updated --- README.md | 37 ++++++++++++++++++++++++++++++++++++- src/QueryEditor.tsx | 18 +++++++++++------- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9e03b6c..42f4e07 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Note that `PER PARTITION LIMIT 1` used instead of `LIMIT 1` to query one row for ### Alerting Alerting is supported, however it has some limitations. Grafana does not support long(narrow) series in alerting, -so query result must be converted to wide series before hading it over to grafana. Datasource performs it in pretty +so query result must be converted to wide series before handing it over to grafana. Datasource performs it in pretty simple way - it creates labels using all the non-timeseries field and then removes that fields from response. Basically, this query(using example table) ``` @@ -210,6 +210,41 @@ AND registered_at > $__unixEpochFrom AND registered_at < $__unixEpochTo * `dateOf(maxTimeuuid(registered_at*1000))` used to convert seconds to milliseconds(`registered_at*1000`) and then to convert milliseconds to `timestamp` type, which is handed over to grafana. * `$__unixEpochFrom` and `$__unixEpochTo` are variables with unix time in the seconds format that are used to fill out conditions part of the query. +#### Cassandra fat partitions +Cassandra stores data in `partitions` which are minimal storage units for the DB. It means that using the example table +``` +CREATE TABLE IF NOT EXISTS temperature ( + sensor_id uuid, + registered_at timestamp, + temperature int, + PRIMARY KEY ((sensor_id), registered_at) +); +``` +will lead to partitions bloating and performance degradation, because all the data for all time for specific `sensor_id` is stored in just one partition(first part of `PRIMARY KEY` is `PARTITION KEY`). +To avoid that there is a technique called `bucketing`, which basically means that partitions are split up into smaller pieces. +For instance, we can split that example table partitions by time: year, month, day, or even hour and less. What to choose depends on how +much data stored in each partition. To achieve that the example table has to be modified like this: +``` +CREATE TABLE IF NOT EXISTS temperature ( + sensor_id uuid, + date date, + registered_at timestamp, + temperature int, + PRIMARY KEY ((sensor_id, date), registered_at) +); +``` +After that change the database schema became more effective because of bucketing by date, and queries will have a form of +``` +SELECT sensor_id, temperature, registered_at +FROM temperature +WHERE sensor_id IN (99051fe9-6a9c-46c2-b949-38ef78858dd1, 99051fe9-6a9c-46c2-b949-38ef78858dd0) +AND date = '${__from:date:YYYY-MM-DD}' +AND registered_at > $__timeFrom +AND registered_at < $__timeTo +``` +Note that `$__from`/`$__to` variables are used. They are [grafana built-in variables](https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#__from-and-__to), and they have formatting capabilities which are perfect for our case. +In case when time range includes more than one day, each day has to be added into `AND date IN (...)` predicate. Another way to make it more convenient is to consider using larger buckets, e.g. month instead of day-size. + ## Development [Developer documentation](https://github.com/HadesArchitect/GrafanaCassandraDatasource/wiki/Developer-Guide) diff --git a/src/QueryEditor.tsx b/src/QueryEditor.tsx index d00dad6..fac9e5a 100644 --- a/src/QueryEditor.tsx +++ b/src/QueryEditor.tsx @@ -1,8 +1,8 @@ -import React, { ChangeEvent, PureComponent, FormEvent } from 'react'; -import { Button, InlineField, InlineFieldRow, Input, InlineSwitch, Select, TextArea } from '@grafana/ui'; -import { QueryEditorProps, SelectableValue } from '@grafana/data'; -import { CassandraDatasource, CassandraDataSourceOptions } from './datasource'; -import { CassandraQuery } from './models'; +import React, {ChangeEvent, FormEvent, PureComponent} from 'react'; +import {Button, InlineField, InlineFieldRow, InlineSwitch, Input, Select, TextArea} from '@grafana/ui'; +import {CoreApp, QueryEditorProps, SelectableValue} from '@grafana/data'; +import {CassandraDatasource, CassandraDataSourceOptions} from './datasource'; +import {CassandraQuery} from './models'; type Props = QueryEditorProps; @@ -42,6 +42,10 @@ export class QueryEditor extends PureComponent { props.query.valueId && props.query.valueId !== '' ) { + // @ts-ignore + this.props.queries.forEach(function (value) { + console.log("TYPE: %s", value.queryType) + }) this.props.onRunQuery(); } } @@ -150,7 +154,7 @@ export class QueryEditor extends PureComponent { const options = this.props; this.props.query.queryType = 'query'; - if (this.props.app === 'unified-alerting') { + if (this.props.app !== undefined && this.props.app === CoreApp.UnifiedAlerting) { this.props.query.queryType = 'alert'; } @@ -162,7 +166,7 @@ export class QueryEditor extends PureComponent {