# Case: Cogenius meterdata
## Doel van dit verslag
In dit verslag wordt samengevat wat er precies is uitgevoerd in het kader van deze onderzoekscase. Dit document biedt een antwoord op de vragen die door Cogenius werden gesteld.
## Aanpak
We hebben samen met jullie de huidige architectuur bekeken. We hebben ons vooral gefocust op de manier waarop de (meter)data momenteel wordt opgeslagen. De data zit tot op heden opgeslagen in een Azure MSSQL database.
Het probleem hiermee is dat deze database relatief duur is en niet oneindig schaalbaar is. Het is ook zo dat dit niet de beste keuze is om time-series data in op te slaan. Daarnaast wil Cogenius evolueren naar een architectuur die meer mogelijkheden biedt, denk bv. aan real-time inzichten.
Het Sizing Servers Lab kreeg toegang tot een data-export (JSON). We hebben deze bekeken en op basis van onze ervaring gekozen om een proof of concept (poc) uit te werken met behulp van InfluxDB.
### InfluxDB architectuur
De architectuur van InfluxDB werd eerst en vooral grondig bestudeerd. Versie 1 is de huidige stable release, van versie 2 kwam er zopas een eerste release candidate uit (29/09/2020). Er is eveneens een open-source versie (OSS), een cloud versie en een enterprise versie. De verschillen werden reeds besproken in een apart documentje (zie bijlagen).
In samenspraak met Cogenius werd beslist om te focussen op InfluxDB v2.
De architectuur werd uitvoerig toegelicht in een presentatie. De slides hiervan zijn eveneens toegevoegd in bijlage, alsook een link naar de opname van deze presentatie.

<small>Screenshot uit presentatie TICK-Stack</small>
## Proof of concept
Er werd een lokale proof of concept (poc) opgezet. Deze kan eenvoudig vertaald worden naar een cloud-aanpak.
Bij de poc werd vooral gekeken naar:
* Kan de data (eenvoudig) opgeslagen worden in InfluxDB
* Hoe de data kan opgeslagen en bevraagd worden
* Wat zijn de pitfalls:
* Is alle data correct weggeschreven?
* Werken de libraries zoals verwacht?
* Werkt InfluxDB zoals verwacht?
* Is de performance aanvaardbaar?
Om dit te testen werd gebruik gemaakt van de data die door Cogenius is aangeleverd. De antwoorden, onze bevindingen, worden verder in dit verslag besproken.
Er werd gebruik gemaakt van drie virtuele machines:
* Machine om InfluxDB te hosten
* Machine om Python scripts mee uit te voeren (Jupyter Lab)
* Machine om Grafana te hosten
De gebruikte code kan teruggevonden worden in bijlage.
## Data wegschrijven
Om data weg te schrijven wordt er bij InfluxDB gebruik gemaakt van het zogenaamde "line-protocol". Dit wordt eveneens besproken in de presentatie.
De libraries laten echter toe om met andere data-formaten te werken, bv. JSON. Maar, eender welk formaat zal altijd vertaald worden naar dit line-protocol.

<small>Screenshot - voorbeeld line protocol</small>
### Concrete tips om efficiënt data weg te schrijven:
* Werk met het line-protocol formaat
* Rangschik je data volgens tijd (timestamp)
* Rangschik je tags alfabetisch
* Gebruik de juiste tijdsprecisie: bv. seconden
* Werk met bulks, niet lijn-per-lijn
Deze zaken werder uitvoerig besproken tijdens de presentatie.
Volgende code-snippet toont aan hoe dit bv. kan:
```python=
def parse_json(gsrn, json_list):
rows = []
for row in json_list:
for readings in row['readings']:
timestamp = int((UTC.localize(ciso8601.parse_datetime(readings['datetime'])) - EPOCH).total_seconds())
rows.append(Point("test-" + dt_string) \
.tag("category", row['category']) \
.tag("gsrn", gsrn) \
.tag("method", row['method']) \
.tag("quality", readings['quality']) \
.tag("timeframe", row['timeframe']) \
.tag("unit", row['unit']) \
.field("value", readings['value']) \
.time(timestamp, WritePrecision.S) \
.to_line_protocol())
return rows
```
### Mogelijke problemen bij wegschrijven data:
**Precision**
De "precision" zorgde in eerste instantie voor problemen, dit bleek een bug te zijn in de Python library (deze is intussen verholpen). Daarnaast is het van belang dat de timestamps in de UTC/GMT timezone zijn omgezet. Men raadt hiervoor de ciso8601 library te gebruiken, aangezien dit een wrapper is rond C-code (zie snippet hierboven).
Het is mogelijk om InfluxDB zelf de timestamp te laten bepalen. Om dit te doen laat je de timestamp (uit line-protocol) achterwege. Doch is het wellicht verstandiger om dit op de client te bepalen, zodanig dat de timestamp met zekerheid correct is. Het is verstandig om op de client NTP te activeren, zie bv. [hier](https://linuxconfig.org/ubuntu-20-04-ntp-server) hoe dat kan op Ubuntu 20.04.
**Batching**
Zoals uitgelegd in de presentatie is het niet aangeraden om lijn per lijn data weg te schrijven maar dit in bulks / batches te doen. De library heeft hiervoor automatisch ingebouwde oplossingen, maar dit blijkt niet goed te werken. Ook al lijkt het dat alle data correct is weggeschreven, dat is niet het geval.
Er werd wel degelijk data weggeschreven bij bachting, maar niet alle data. Het lijkt erop dat de thread te vroeg de data "loslaat" waardoor er data-loss ontstaat. Het is dus onvoorspelbaar hoeveel data precies weggeschreven is, alsook worden er geen fouten (exceptions) teruggegeven.
```python
write_api = client.write_api(write_options=WriteOptions(write_type=WriteType.batching, batch_size=5_000, flush_interval=1_000))
write_api.write(bucket=bucket_name, record=line, write_precision=WritePrecision.S)
```
Wat daarentegen wel prima werkt:
```python
client.write_api(write_options=WriteOptions(write_type=WriteType.synchronous))
write_api.write(bucket=bucket_name, record=lines, write_precision=WritePrecision.S)
```
Merk op dat `lines` een list is, die dus meerdere data punten bevat.
**Compressie**
InfluxDB zal de data automatisch comprimeren waar mogelijk (zie presentatie). Anderzijds is het verstandig om compressie langs client side in te schakelen. Zeker in het geval met InfluxDB Cloud (minder data versturen). Je stelt dit in bij het aanmaken van de client:
```python
client = InfluxDBClient(url="http://10.10.20.62:8086", token=token, org=org, enable_gzip=True, debug=False) # debug=True
```
**Overloading**
Wanneer er te veel data tegelijk wordt weggeschreven kan dit problemen met zich meebrengen. De poc is hierin natuurlijk ietwat onrealistisch: wij proberen de gehele dataset zo snel als mogelijk te verwerken. In de praktijk zal Cogenius veel minder data per keer wegschrijven.
Wanneer er te veel load wordt veroorzaakt zal dit resulteren in exceptions. Denk daarbij aan time-outs, connection errors, ... . Deze kunnen natuurlijk opgevangen worden en hier kan dan naar gehandeld worden. Wanneer dit echter wordt genegeerd kan dit voor serieuze problemen zorgen.
Wat dan opvalt is dat zowel de CPU als het RAM en de storage onder serieuze druk komen te staan. Dit zal resulteren in vele exceptions. Wanneer de load dan niet zakt, zal het RAM vollopen waardoor het proces (= InfluxDB) zal gekilled worden door de kernel, m.a.w. InfluxDB crasht. Dit is geheel normaal.
Deze crash is uiterst interessant. Wanneer we InfluxDB opnieuw opstarten, verloopt dit zeer vlot en hebben we geen data-verlies vastgesteld. Dit is dus perfect.
Merk op, gegeven dat jullie aan de slag gaan met de cloud versie is deze problematiek minder relevant (doch is het interessant dit te weten: InfluxDB werkt stabiel). InfluxDB Cloud heeft een aantal "usage limites" opgesteld die eventueel kunnen verhoogd worden (zie [documentatie](https://docs.influxdata.com/influxdb/v2.0/account-management/pricing-plans/)).
Die limieten worden afgedwongen via een proxy. De proxy is gebaseerd op de zeer gekende, stabiele Nginx software. Concreet: wanneer er te veel data verstuurd zal worden, zal deze proxy voorkomen dat InfluxDB overladen wordt en krijgt de client duidelijke errors terug.
**Duplicates**
De export die wij hebben verkregen bevat "duplicates", punten die exact dezelfde zijn. InfluxDB zal in dit geval de data overschrijven met dezelfde data.
De dataset bevat vermoedelijk ook wat kleinere fouten. Er zijn punten teruggevonden waarbij alle tags (category, gsrn, method, quality, timeframe en unit) dezelfde zijn, alsook de timestamp, maar wel een verschillende value. In dit geval zal de data eveneens overschreven worden. Wellicht is voor die specifieke records de quality verkeerd ingesteld.
```
test-2020-09-18_15_58_06,category=Ediel,gsrn=541449011000010633,method=ActiveConsumption,quality=Definitive,timeframe=TotalHours,unit=KWT value=0.0 1420585200
test-2020-09-18_15_58_06,category=Ediel,gsrn=541449011000010633,method=ActiveConsumption,quality=Definitive,timeframe=TotalHours,unit=KWT value=44.74 1420585200
```
<small>Voorbeeld "dubbele" data</small>
Merk op dat het overschrijven van data (feitelijk een update) een zware operatie is voor InfluxDB. Maar, voor jullie case, mits juist gebruik van "quality" vormt dit geen enkel probleem.
## Data bevragen
In v1 kan er gebruik gemaakt worden van InfluxQL, en vanaf versie 1.8 is daar Flux bijgekomen. In v2 kan (momenteel?) uitsluitend gebruikt gemaakt worden van Flux.
InfluxQL is zeer gelijkaardig aan "klassieke" SQL-statements. Flux is een andere query-taal, specifiek ontworpen voor het bevragen van time-series. De taal is gelukkig ook eenvoudig te leren en te begrijpen.
Query's kunnen uiteraard via code uitgevoerd worden maar even goed via een grafische tool / dashboard. InfluxDB v2 heeft daarvoor out-of-the-box Chronograf (in v1 moet dit apart geïnstalleerd worden).
### Chronograf problemen
Chronograf is enkel en alleen geschikt i.s.m. TICK-stack (Telegraf, InfluxDB, **C**hronograf, Kapacitor) . Dit kan een probleem zijn wanneer er gebruik gemaakt wordt van andere datasources (bv. MSSQL).
Daarnaast is de ene versie (doelende op de verschillende beta-versies en de laatste release candidate [rc]) al stabieler dan de andere. Beta 16 was prima bruikbaar, terwijl bij de laatste rc voortdurend problemen optraden: kan niet langer overweg met grotere hoeveelheden data.
We stellen vast dat bugs snel worden verholpen, maar het is moeilijk te voorspellen hoe dit voor Chronograf zal uitdraaien.
Chronograf heeft echter wel een handige feature aan boord: de "Query Builder". Met deze tool is het eenvoudig om Flux query's te genereren.
### Grafana
Gelukkig is er een alternatief. Eentje dat ook nog is een hele brede adoptiegraad kent en eveneens ontelbare mogelijkheden heeft: Grafana. Sinds versie 7.0 is er in Grafana "native support" voor InfluxDB 2, lees: Flux query's.
In Grafana kan de data perfect opgevraagd worden en indien gewenst gevisualiseerd worden (zelfs wanneer het om ontzettend veel data gaat).

<small>Screenshot Grafana: alle meterdata van Elexys export</small>.
#### Instellingen
Als het om een "stevige" query gaat, kan het even duren om al deze data te ontvangen. De dataproxy heeft standaard een timeout van 30 seconden, wat veel te laag is.
```
# /etc/grafana/grafana.ini
#################################### Data proxy ###########################
[dataproxy]
# This enables data proxy logging, default is false
;logging = false
# How long the data proxy waits before timing out, default is 30 seconds.
# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set.
timeout = 7200
```
#### Manipulaties
In versie 7 van Grafana kan je eenvoudiger de data achteraf nog manipuleren. Dus, zonder de Flux query aan te passen, laat staan opnieuw uit te voeren.
Als je bv. de velden wilt hernoemen kan je gebruik maken van "Transform" actions. Een "outer join" op "Time" en dan een "Organize fields". Bepaalde reeksen kan je desgewenst ook verbergen.

<small>Screenshot voorbeeld transformatie</small>
Eventueel kan in een ander, nieuw panel zelfs data uit een ander panel worden gebruikt. Daarvoor dient als databron gekozen worden voor bv. een dashboard.

<small>Screenshot databron dashboard</small>
## InfluxDB Cloud
Om gebruik te maken van InfluxDB (en Chronograf) in Azure kan er best gebruik gemaakt worden van InfluxDB Cloud. Dat wil zeggen dat je klant bij InfluxData wordt, maar gebruik maakt van Microsoft Azure. Op aanvraag kan InfluxDB gekoppeld worden aan de juiste VNet.

<small>Screenshot keuze cloud platform</small>
De voorbeeldcode kan eenvoudig aangepast worden: de token, de organisation en url moeten naar die van de cloud omgeving worden aangepast. Voor de rest werkt alles nagenoeg identiek.
```
token = "i_RMh4kqM9ZGJdpo6vAEctaOugxCmPBGOjvVq-UTKulgLldTMPqx6BmZfErjhsXLtkbgeflcd34Z8mpiNVDsEg=="
org = "bikjivmggyvizhkfod@niwghx.online"
bucket = "test_data"
client = InfluxDBClient(url="https://westeurope-1.azure.cloud2.influxdata.com", token=token, org=org)
```
<small>Aanpassen connectie parameters</small>
## Algemene conclusie
Wij zijn er van overtuigd dat InfluxDB een goede match is met de eisen die Cogenius stelt aan het opslagsysteem. Uit onze poc is gebleken dat InfluxDB een erg matuur, stabiel product is geworden. Zelfs de versie die momenteel nog niet toe is aan een global available release.
Door de adviezen die in dit verslag zijn opgenomen denken wij dat we jullie hiermee voldoende hebben geïnformeerd om de juiste keuzes te maken.
Tevens kregen jullie van ons nog een aantal andere documenten (in bijlage) die dit onderzoek versterken. Alsook staan hier voldoende voorbeelden in om aan de slag te gaan met InfluxDB.