Skip to content

Commit

Permalink
Raw query tip and README.md updated
Browse files Browse the repository at this point in the history
  • Loading branch information
unflag committed Oct 23, 2023
1 parent b33727e commit 96afa7a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 8 deletions.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```
Expand Down Expand Up @@ -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)
18 changes: 11 additions & 7 deletions src/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -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<CassandraDatasource, CassandraQuery, CassandraDataSourceOptions>;

Expand Down Expand Up @@ -42,6 +42,10 @@ export class QueryEditor extends PureComponent<Props> {
props.query.valueId &&
props.query.valueId !== ''
) {
// @ts-ignore
this.props.queries.forEach(function (value) {
console.log("TYPE: %s", value.queryType)
})
this.props.onRunQuery();
}
}
Expand Down Expand Up @@ -150,7 +154,7 @@ export class QueryEditor extends PureComponent<Props> {
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';
}

Expand All @@ -162,7 +166,7 @@ export class QueryEditor extends PureComponent<Props> {
<InlineField
label="Cassandra CQL Query"
labelWidth={30}
tooltip="Enter Cassandra CQL query. You can use $__timeFrom/$__timeTo and $__unixEpochFrom/$__unixEpochTo variables, they will be replaced by chosen range"
tooltip="Enter Cassandra CQL query. There are $__timeFrom/$__timeTo, $__unixEpochFrom/$__unixEpochTo and $__from/$__to variables to dynamically limit time range in queries. You should always use them to avoid excessive data fetching from DB."
grow
>
<TextArea
Expand Down

0 comments on commit 96afa7a

Please sign in to comment.