Seite wählen

Ergebnisse für " 126 "

Danke 2019, danke NETWAYS!

Der Blick zurück auf das vergangene Jahr fällt mir dieses Mal besonders leicht. Denn 2019 war für mich persönlich ein absolut erlebnisreiches Jahr, das ich wohl in ewiger Erinnerung behalten werde. Der Grund dafür ist v.a. das erste Halbjahr (genauer 30.12.2018 – 28.06.2019), in dem ich eine Reise rund um die Welt durch insgesamt 17 Länder gemacht habe. Meine Route führte dabei von Zentral- und Südamerika über Neuseeland bis in den Südpazifik.

Der gewünschte Reisezeitraum von einem halben Jahr stand für mich aus verschiedenen Gründen relativ schnell fest: Ein Jahr wäre mir zu lange gewesen und ein halbes Jahr schien aufgrund der Auslandskrankenversicherung und der Dauer der Untervermietung der Wohnung als der perfekte Zeitraum. Auch die Abwesenheit vom Beruf wäre bei einer längeren Abwesenheit problematischer gewesen.

Stellt sich natürlich die Frage, wie es mit einem Vollzeitjob überhaupt möglich ist, so lange frei zu bekommen? In manchen Firmen existiert dafür eine feste Regelung, wie so ein „Sabbatical“ auszusehen hat, nicht so bei NETWAYS. Allerdings ist das kein Nachteil! Und so habe ich meinen Wunsch nach einer Auszeit im i.d.R. jährlich stattfindenden Mitarbeitergespräch frühzeitig angesprochen. Nach 2-3 weiteren Sitzungen war dann eine Lösung gefunden, wie man Resturlaub, neue Urlaubstage und unbezahlten Urlaub so aufteilen kann, damit es für alle Seiten passt. Im Endeffekt ist es eben nur eine finanzielle Frage, auf wie viel man verzichten kann und möchte.

Meine Tätigkeit hier setzt sich jeweils ca. zur Hälfte aus Kundenterminen und Aufgaben im Büro zusammen. Für den Zeitraum wurden bei mir entweder keine neuen Kundentermine vereinbart bzw. Kundentermine aus langfristigen Projekten im Vorfeld auch an Kollegen übergeben. Im Büro wurde ich von Kollegen vertreten.

NETWAYS@Machu Picchu, Peru

Da ich am Freitag, den 28.06.2019 über Dubai (Vereinigte Arabische Emirate) aus Auckland (Neuseeland) zurück gekommen war, konnte ich das Wochenende nutzen, um meine privaten Angelegenheiten zu regeln. Montag darauf ging’s dann nach dem Umzug im neuen Büro schon gleich wieder voll los. Zugegeben waren die ersten Tage schon stark gewöhnungsbedürftig, aber nach ungefähr einer Woche war es quasi so, als ob ich nie weg gewesen wäre.

Natürlich bleiben für mich von der Reise viele Erlebnisse, Informationen und Bekanntschaften, von denen ich noch lange zehren werde und für die ich NETWAYS und v.a. auch den Kollegen hier unheimlich dankbar bin! Immerhin ist mein Reisefieber mit dem Besuch der PuppetConf 2014 in San Francisco (USA) erst so richtig ausgebrochen. Auch dazwischen hatte ich schon ein paar Gelegenheiten, über NETWAYS die Welt kennen zu lernen. So hat diese Firma nicht nur in beruflicher Hinsicht mein Leben verändert.

„Die Welt ist ein Buch. Wer nicht reist, sieht nur eine Seite davon.“ (Augustinus Aurelius)

Markus Waldmüller
Markus Waldmüller
Head of Strategic Projects

Markus war bereits mehrere Jahre als Sysadmin in Neumarkt i.d.OPf. und Regensburg tätig. Nach Technikerschule und Selbständigkeit ist er nun Anfang 2013 bei NETWAYS als Senior Manager Services gelandet. Seit September 2023 kümmert er sich bei der NETWAYS Gruppe um strategische Projekte. Wenn er nicht gerade die Welt bereist, ist der sportbegeisterte Neumarkter mit an Sicherheit grenzender Wahrscheinlichkeit auf dem Mountainbike oder am Baggersee zu finden.

Schneller LIKEn

Nein, hier soll es nicht um Twitter, Instagram oder Youtube gehen, sondern um Datenbankabfragen in PostgreSQL wie diese:

blog # SELECT * FROM kunden WHERE vorname LIKE 'Ann%';

Diese Abfragen sind recht häufig anzutreffen, man denke z.B. an Drop-Down-Boxen, die z.B. per AJAX mit Vorschlägen gefüllt werden, sobald drei oder mehr Buchstaben eingegeben wurden.

Das Spielfeld

Unsere Beispieldaten enthalten 1.000.000 zufällig generierte Kunden in dieser Form und mit dieser Verteilung von Vornamen, die mit ‚Ann‘ beginnen:

blog # \d kunden
                               Table "public.kunden"
┌────────────┬─────────┬───────────┬──────────┬────────────────────────────────────┐
│   Column   │  Type   │ Collation │ Nullable │              Default               │
├────────────┼─────────┼───────────┼──────────┼────────────────────────────────────┤
│ id         │ integer │           │ not null │ nextval('kunden_id_seq'::regclass) │
│ vorname    │ text    │           │ not null │                                    │
│ nachname   │ text    │           │ not null │                                    │
│ strasse    │ text    │           │ not null │                                    │
│ hausnummer │ integer │           │ not null │                                    │
│ plz        │ text    │           │ not null │                                    │
│ ort        │ text    │           │ not null │                                    │
│ bundesland │ text    │           │ not null │                                    │
└────────────┴─────────┴───────────┴──────────┴────────────────────────────────────┘
Indexes:
    "kunden_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "kunden_plz_check" CHECK (length(plz) = 5)

blog # vorname,count(*) FROM kunden WHERE vorname LIKE 'Ann%' GROUP BY vorname;
┌───────────┬───────┐
│  vorname  │ count │
├───────────┼───────┤
│ Anni      │   963 │
│ Annabella │   984 │
│ Anne      │   965 │
│ Annalena  │   971 │
│ Annika    │  1017 │
│ Anna      │  1011 │
│ Ann       │  1003 │
│ Annelie   │   976 │
│ Annemarie │  1001 │
│ Annabell  │   996 │
└───────────┴───────┘
(10 rows)

Ein erster Versuch – „vanilla“

Schauen wir doch mal, wie unsere PostgreSQL-Datenbank eine Suche nach ‚Ann%‘ bearbeitet:

blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌────────────────────────────────────────────────────────────────────┐
│                           QUERY PLAN                               │
├────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=10244 width=65)      |
|      (actual time=0.019..90.620 rows=9887 loops=1)                 │
│   Filter: (vorname ~~ 'Ann%'::text)                                │
│   Rows Removed by Filter: 990113                                   │
│ Planning time: 0.100 ms                                            │
│ Execution time: 90.956 ms                                          │
└────────────────────────────────────────────────────────────────────┘
(5 rows)

Ein Seq Scan, also der gefürchtete Sequential-Scan aka. Full table scan; alle Datensätze werden gelesen und ‚vorname‘ mit ‚Ann%‘ verglichen. Das ist sehr ineffektiv.

Ein Index muss her!

Die Lösung ist offensichtlich: wenn solche Abfragen häufig vorkommen, muss ein Index her. Der wird den Vorgang beschleunigen:

blog # CREATE INDEX vorname_btree_vanilla ON kunden (vorname);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌───────────────────────────────────────────────────────────────────────┐
│                            QUERY PLAN                                 │
├───────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=10244 width=65)         |      
|      (actual time=0.011..105.340 rows=9887 loops=1)                   │
│   Filter: (vorname ~~ 'Ann%'::text)                                   │
│   Rows Removed by Filter: 990113                                      │
│ Planning time: 0.195 ms                                               │
│ Execution time: 105.768 ms                                            │
└───────────────────────────────────────────────────────────────────────┘
(5 rows)

Uhm, Moment mal… warum nimmt meine Datenbank nicht den Index zu Hilfe?!? Geht es denn mit einzelnen Werten?

 blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname IN ('Anna','Anne','Annelie');
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                              QUERY PLAN                                                │
├────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=59.83..6892.21 rows=2909 width=65)                   |
|      (actual time=1.275..5.054 rows=2952 loops=1)                                      │
│   Recheck Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                        │
│   Heap Blocks: exact=2656                                                              │
│   ->  Bitmap Index Scan on vorname_btree_vanilla  (cost=0.00..59.10 rows=2909 width=0) |
|      (actual time=0.652..0.652 rows=2952 loops=1)                                      │
│         Index Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                    │
│ Planning time: 0.136 ms                                                                │
│ Execution time: 5.292 ms                                                               │
└────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

Ja, da wird der Index genommen, und die Ausführung ist auch gleich um Größenordnungen schneller.

Was ist also das Problem?

„C“ und seine Spätfolgen – Schei* encoding!

Das Geheimnis liegt – wie so häufig – in der Lokalisierung. Btree-Indexe sind (für Text-Daten) auf das C-Locale hin optimiert. Wenn aber die Datenbank (wie heutzutage üblich!) mit en_US.UTF8 oder de_DE.UTF8 initialisiert wurde, müssen wir dem Index bei der Erstellung mitteilen, dass wir pattern operator-Aktionen ausführen können wollen. PostgreSQL kommt mit einem ganzen Haufen dieser Operator Classes.

Für unser TEXT-Feld ‚vorname‘ nehmen wir text_pattern_ops. Nach der Erstellung testen wir, ob der Index unsere LIKE-Anfrage beschleunigt und verifizieren, dass auch weiterhin die klassischen <=, == und >= Vergleichsoperatoren funktionieren:

blog # DROP INDEX vorname_btree_vanilla ;
blog # CREATE INDEX vorname_btree_opclass ON kunden (vorname text_pattern_ops);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                     QUERY PLAN                                          │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=129.19..10234.85 rows=10244 width=65)                 |
|       (actual time=5.327..16.083 rows=9887 loops=1)                                     │
│   Filter: (vorname ~~ 'Ann%'::text)                                                     │
│   Heap Blocks: exact=6830                                                               │
│   ->  Bitmap Index Scan on vorname_btree_opclass  (cost=0.00..126.62 rows=5820 width=0) |
|       (actual time=3.524..3.524 rows=9887 loops=1)                                      │
│         Index Cond: ((vorname ~>=~ 'Ann'::text) AND (vorname ~<~ 'Ano'::text))          │
│ Planning time: 0.378 ms                                                                 │
│ Execution time: 16.650 ms                                                               │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname IN ('Anna','Anne','Annelie');
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                        QUERY PLAN                                       │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=59.83..6892.21 rows=2909 width=65)                    |
|       (actual time=1.233..5.001 rows=2952 loops=1)                                      │
│   Recheck Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                         │
│   Heap Blocks: exact=2656                                                               │
│   ->  Bitmap Index Scan on vorname_btree_opclass  (cost=0.00..59.10 rows=2909 width=0)  |
|       (actual time=0.634..0.634 rows=2952 loops=1)                                      │
│         Index Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                     │
│ Planning time: 0.135 ms                                                                 │
│ Execution time: 5.246 ms                                                                │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

Wunderbar! Und 17ms klingt auch gleich viel besser als 100ms.

Geht da noch was?

Jedes Kind weiß, dass Indexe nur Anfragen wie LIKE ‚Ann%‘ beschleunigen können. Für LIKE ‚%nna%‘ gibt es leider keine Hilfe von der Datenbank. Ist ja auch irgendwie klar, der Btree muss ja von links nach rechts aufgebaut werden…

EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE '%nna%';
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                         QUERY PLAN                                      │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=19022 width=65)                           |
|      (actual time=0.014..110.607 rows=11993 loops=1)                                    │
│   Filter: (vorname ~~ '%nna%'::text)                                                    │
│   Rows Removed by Filter: 988007                                                        │
│ Planning time: 0.131 ms                                                                 │
│ Execution time: 111.006 ms                                                              │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(5 rows)

Aber stimmt das? Gibt es wirklich keine Möglichkeit, solche Abfragen zu beschleunigen?

PostgreSQL ist schier unfassbar erweiterbar, und unter anderem kommt es von Haus aus mit einer Extension pg_trgm, die wiederum operator classes für GIN und GiST Indexe mitbringt.

blog # CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION
blog # CREATE INDEX vorname_gin_trgm ON kunden USING GIN (vorname gin_trgm_ops);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE '%nna%';
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                           QUERY PLAN                                     │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=183.42..13123.52 rows=19022 width=65)                  |
|       (actual time=5.049..15.935 rows=11993 loops=1)                                     │
│   Recheck Cond: (vorname ~~ '%nna%'::text)                                               │
│   Heap Blocks: exact=7670                                                                │
│   ->  Bitmap Index Scan on vorname_gin_trgm  (cost=0.00..178.67 rows=19022 width=0)      |   
|       (actual time=3.014..3.014 rows=11993 loops=1)                                      │
│         Index Cond: (vorname ~~ '%nna%'::text)                                           │
│ Planning time: 0.253 ms                                                                  │
│ Execution time: 16.488 ms                                                                │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

pg_trgm kann noch mehr

Eine vermeintlich nette Spielerei, aber – wenn man es denn kennt – in vielen Situationen hilfreich, ist die Ähnlichkeitssuche, die pg_trgm in Form von Funktionen und Operatoren mitbringt:

blog # SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌─────────┬───────┐
│ vorname │ count │
├─────────┼───────┤
│ Nick    │   995 │
│ Nico    │  1047 │
│ Nicole  │   977 │
│ Nicolas │  1026 │
└─────────┴───────┘
(4 rows)

Und auch hier beschleunigt der GIN-Index die Abfrage signifikant:

blog # EXPLAIN ANALYSE SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                          QUERY PLAN                                      │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Sort  (cost=24761.50..24763.07 rows=630 width=18)                                        |
|      (actual time=915.696..915.697 rows=4 loops=1) .                                     │
│   Sort Key: (similarity(vorname, 'Nick'::text)) DESC                                     │
│   Sort Method: quicksort  Memory: 25kB                                                   │
│   ->  GroupAggregate  (cost=24716.83..24732.20 rows=630 width=18)                        |
|      (actual time=915.305..915.689 rows=4 loops=1)                                       │
│         Group Key: vorname                                                               │
│         ->  Sort  (cost=24716.83..24719.33 rows=1000 width=6)                            |
|      (actual time=915.162..915.305 rows=4045 loops=1)                                    │
│               Sort Key: vorname                                                          │
│               Sort Method: quicksort  Memory: 286kB                                      │
│               ->  Seq Scan on kunden  (cost=0.00..24667.00 rows=1000 width=6)            |
|      (actual time=0.650..914.065 rows=4045 loops=1)                                      │
│                     Filter: (vorname % 'Nick'::text)                                     │
│                     Rows Removed by Filter: 995955                                       │
│ Planning time: 0.296 ms                                                                  |
│ Execution time: 915.737 ms                                                               │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(13 rows)

blog # CREATE INDEX vorname_gin_trgm ON kunden USING GIN (vorname gin_trgm_ops);
blog # EXPLAIN ANALYSE SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                             QUERY PLAN                                   │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Sort  (cost=3164.18..3165.75 rows=630 width=18)                                          |
|      (actual time=32.510..32.510 rows=4 loops=1)                                         │
│   Sort Key: (similarity(vorname, 'Nick'::text)) DESC                                     │
│   Sort Method: quicksort  Memory: 25kB                                                   │
│   ->  HashAggregate  (cost=3127.01..3134.88 rows=630 width=18)                           |
|      (actual time=32.497..32.503 rows=4 loops=1)                                         │
│         Group Key: vorname                                                               │
│         ->  Bitmap Heap Scan on kunden  (cost=75.75..3122.01 rows=1000 width=6)          |
|      (actual time=5.993..31.447 rows=4045 loops=1)                                       │
│               Recheck Cond: (vorname % 'Nick'::text)                                     │
│               Rows Removed by Index Recheck: 13010                                       │
│               Heap Blocks: exact=9174                                                    │
│               ->  Bitmap Index Scan on vorname_gin_trgm                                  |
|      (cost=0.00..75.50 rows=1000 width=0) (actual time=4.976..4.976 rows=17055 loops=1)  │
│                     Index Cond: (vorname % 'Nick'::text)                                 │
│ Planning time: 0.123 ms                                                                  │
│ Execution time: 32.563 ms                                                                │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(13 rows)

Fazit

Dass PostgreSQL nicht von Haus aus alle LIKE-Anfragen per Index beschleunigt, sorgt gerne für Irritationen. Auf der anderen Seite öffnen sich, sobald man anfängt, sich mit dem Thema auseinanderzusetzen, ganz neue Möglichkeiten, die man in dieser Form bei anderen DBMS nicht findet. Und wir haben noch gar nicht über eine echte Volltextsuche gesprochen!

P.S.: pg_trgm kann kein Chinesisch!

Wenn nicht-alphanumerische Buchstaben in’s Spiel kommen (oder pg_trgm zu langsam ist…) sollte man einen Blick auf PGroonga werfen, das in diesen Bereichen glänzt.

Über den Author

Gunnar “Nick” Bluth hat seine Liebe zu relationalen Datenbanken Ende des letzten Jahrtausends entdeckt. Über MS Access und MySQL 3.x landete er sehr schnell bei PostgreSQL und hat nie zurückgeschaut, zumindest nie ohne Schmerzen. Er verdient seine Brötchen seit beinahe 20 Jahren mit FOSS (Administration, Schulungen, Linux, PostgreSQL). Gelegentlich taucht er auch tiefer in die Programmierung ein, so als SQL-Programmierer bei der Commerzbank oder in App-Nebenprojekten

Das neue User Interface von Icinga DB

Während mit Icinga DB sehr viel unter der Haube passiert ist, kommt mit dem Release auch ein rundum neu geschriebenes Monitoring Modul für Icinga Web 2. In diesem Zuge erhielt auch das User Interface ein ausführliches Redesign. In diesem Blogpost werden die wichtigsten Änderungen erklärt.

Listen

Wie im bewährten Monitoring Modul gibt es im Monitoring Modul für Icinga DB viele Listenansichten. Daher wurden diese komplett überarbeitet. Allen Listenelementen liegt eine grundlegende einheitliche Anatomie zugrunde.

ListItem Anatomy.jpg

Anatomie eines Listenelements

Visual

Für jedes Listenelement wird ein so genanntes Visual verwendet. Dieses dient dazu, in langen Listen bestimmte Elemente hervorzuheben bzw. einen intuitiven Überblick geben, welchen Zustand das zugrunde liegende Objekt hat. Bei Host- bzw. Servicelisten wird dadurch beispielsweise der State dargestellt. So wird in der Übersicht unmittelbar ersichtlich, bei welchen Objekten Probleme vorliegen.

Title

Der Titel beschreibt ergänzend kurz zusammengefasst den Zustand des Listenelements. So enthält er beispielsweise die Info, dass ein Host gerade Down ist. Während das Visual einen intuitiven Eindruck gibt, erklärt der Titel, was genau passiert ist.

Meta

Der Metabereich ist für zusätzliche Informationen vorgesehen. In der Regel werden hier Zeitangaben des Listenelements angezeigt. In Host und Servicelisten steht hier, wie lange sich das Objekt bereits im entsprechenden Zustand befindet.

Caption

Der Caption-Bereich enthält detaillierte Informationen zum Listenelement. Dies kann im Falle von Hosts und Services der Plugin Output sein. Bei Kommentaren und Downtimes werden hier die Kommentartexte des Users angezeigt. Um die Listendarstellung kompakt und einheitlich zu halten, werden lange Texte auf eine oder mehrere Zeilen gekürzt.

Klickbare Elemente

Viele der Listenelemente enthalten neben dem Hauptelement zwei oder mehrere klickbare Elemente. Alle Teile des Listenelements, die auf weitere Detailinformationen verweisen sind eindeutig hervorgehoben. Dadurch ist sofort erkennbar, hinter welchen Textteilen sich Zusatzinformationen befinden.

Overdue Checks in Host- und Service Listen

In Host und Servicelisten werden Overdue Checks nun besonders auffällig hervorgehoben. Dadurch ist auf den ersten Blick sofort ersichtlich, welche Objekte möglicherweise nicht mehr aktuell sind.

Artboard Copy.jpg

Das neue Icinga DB Design: Hostliste und Detailbereich

Detailgrad der Listenansichten wählen

Der Detailgrad der Host- und Servicelisten ist nun wählbar. Die Standardansicht zeigt den Titel und einen zweizeiligen Plugin Output. In der detaillierten Ansicht wird der gesamt Plugin Output angezeigt. Will man einen größeren Überblick bekommen gibt es außerdem die Minimalansicht. Hier wird in einer Zeile der Plugin Output angeschnitten, wenn genügend Platz vorhanden ist. Dafür sieht man auf einem Bildschirm wesentlich mehr Listenelemente als in den anderen Darstellungen.

State Change Visual in History- und Notification Elementen

In History und Notification Listen sind unter anderem Statewechsel-Elemente zu finden. Hier wird im Visual der Wechsel nun auf den ersten Blick deutlich gemacht. Neben dem aktuellen State wird gleichzeitig auch der vorherige State ersichtlich.

State Changes sind in den Notificationlisten nun besser ersichtlich.

Detailansichten

Optimierte Headerbereiche

Die Headerbereiche der einzelnen Objekttypen erhalten ein neues Design. Während die herkömmlichen Headerbereiche sehr viel Platz brauchten, sind die Informationen kompakter.

Graphen

Die Detailansichten der Elemente waren bisher sehr textlastig. Nach dem Redesign sind die Detailbereiche deutlich visueller angelegt. Nun werden anstatt der bloßen Auflistung Informationen kombiniert und Zusammenhänge dargestellt.

Modaldialoge für schnelle Aktionen

Wollte man bisher aus dem Detailbereich einen Kommentar anlegen oder eine Downtime setzen wurde der Dialog in einer weiteren Spalte angezeigt, so dass die Inhalte der linken Spalte verloren gingen. Im neuen Monitoring Modul gibt es nun ein Modal-Element für kurze Interaktionsdialoge. Für Aktionen im Detailbereich erscheint nun ein Modaldialog. Dadurch bleibt die linke Listenspalte und somit der Kontext besser erhalten.

 

Florian Strohmaier
Florian Strohmaier
Senior UX Designer

Mit seinen Spezialgebieten UI-Konzeption, Prototyping und Frontendentwicklung unterstützt Florian das Dev-Team bei NETWAYS. Trotz seines Design-Backgrounds fühlt er sich auch in der Technik zuhause. Gerade die Kombination aus beidem hat für ihn einen besonderen Reiz.

Milano – PostgreSQL Conference Europe 2019

It has been a while since one of us visited a PostgreSQL Conference. So when the opportunity arose Lennart and I took it. Additionally, it is somewhat of a special pgconf this year, because the conference is held where it originated, Italy. With over 500 attendees the conference set a new all time record. It is going to be a good one. Numerous talks, a lot of variety and gaining more insight into the postgres community. That being said, these talks can by no means be summarized in one blog post, that is why I have decided to bring you my just personal highlights. If you are interested in talks not mentioned here, I highly recommend checking out the official pgconf.eu website, where at least most slides of speakers can be found.

#PostGIS #PgConfEU Slides now online here https://t.co/YgDVHC420V https://t.co/ze26KYWiZh

— Paul Ramsey (@pwramsey) October 16, 2019

Paul Ramseys talk about PostGIS was the first one that spiked my attention. Having used PostGIS before without extensive knowledge, the add-on seemed pretty mundane, but little did I know. And I mean that. Little did I know of the extend of PostGIS or GIS as a whole. Let me try to give you the just of it. There was a phrase that he used a few times that somehow stuck: GIS without the GIS. To be just that PostGIS without the GIS the workload of low-performance scripts had to be moved to more efficient SQL. Additionally, spatial middleware from the database to the web had to go. Lastly, PostGIS had to be build in a way that provides developer with direct access to a GIS analytical engine. After giving everyone a basic introduction with the some example problems and queries to solve them. Well, he told us everything about PostGIS or atleast a lot. SFSQL, OSS, ISO, Indexing, Spatial Joins, the list is long. 189 Slides, If you will.

Applications developers are certainly enjoying ORM talk by Louise Grandjonc @louisemeta pic.twitter.com/9iE4MxRelk

— PGConf Europe (@pgconfeu) October 17, 2019

Next up in my little list of talks I really enjoyed was Wonderful SQL features your ORMs can use (or not) by Louise Grandjonc. She went through various ORMs (Object Relationship Mapper) during her talk like Django, SQLAlchemy, activerecord and Sequel and used them for a little project of hers. With scripts she aggregated Data regarding pop music lyrics and used this data as an example for various queries.

More Than a Query Language: SQL in the 21st Century was the title of the talk I want to tell you about next. Markus Winand talked a lot of history here. He looked at the very beginning and saw how SQL evolved and changed in many ways. A lot of empathisis was put on SQL having been bound to the idea of an relational data model for too long and that not necessarily beening the case today anymore. While jumping through the timeline of SQL development and explaining various features in the process he validated his claim.

I think it was a great event. I really liked the variety of topics and I hope even though I just mentioned a few, you got some insight into the event. PostgreSQL has an awesome community and having that conference as a platform to exchange experience, help and learn from eachother is just great

Alexander Stoll
Alexander Stoll
Consultant

Alex hat seine Ausbildung zum Fachinformatiker für Systemintegration bei NETWAYS Professional Services abgeschlossen und ist nun im Consulting tätig. Vereinzelt kommt es auch vor das er an Programmierprojekten mitarbeitet. Auch privat setzt er sich sehr viel mit Informationstechnologie auseinander, aber jenseits davon ist auch viel Zeit für Fußballabende, Handwerkerprojekte und das ein oder andere Buch.

Entwicklerazubis: Jahr 1, das Projekt

Man bekommt nicht immer das, was man will… außer, man ist bei NETWAYS Azubi!

 

Mein erstes Jahr bei NETWAYS ist fast vorüber, und meine jugendlichen Ambitionen haben den Wunsch in mir erweckt, mit den beiden anderen Dev-Azubis meines Jahres, Loei und Niko, ein eigenes Projekt zu übernehmen, um unsere erlernten Fertigkeiten zu prüfen, eine Übersicht davon zu bekommen, auf welchem Stand wir alle sind, den ein oder anderen Trick voneinander aufzuschnappen und eigenverantwortlich und kooperativ ein Projekt zu vervollständigen. Und alles, was es gebraucht hat, um diesen Wunsch zu erfüllen, war diese Idee zu pitchen und klar und deutlich zu vermitteln, warum dieses Interesse besteht, und was wir uns davon erhoffen. Meine Vorgesetzten scheinen von der Idee zumindest angetan genug gewesen zu sein, dass wir drei Wochen eingeräumt bekommen haben, um uns um dieses Projekt zu kümmern. Danke, NETWAYS!

Die Aufgabe klingt einfach, hat es aber doch durchaus in sich – aus Berichten in Icinga sollen PDFs generiert werden. Die Funktionalität ist schon vorhanden – unsere Aufgabe ist es, den Benutzern mehr Features zur Verfügung zu stellen, damit man eigene Anpassungen an dem Aussehen der PDFs vornehmen kann. Das Schöne an dieser Aufgabe ist die Breite der Elemente und Bereiche, welche eine Rolle dabei spielen. Icingaweb, PHP, HTML, CSS, Google Chrome… es ist in gewisser Art und Weise eine große Reise in das Unbekannte, und natürlich können wir alle drei uns nicht sicher sein, welche Probleme und Hürden sich uns in den Weg stellen werden, und wie die zündenen Ideen kommen, die uns helfen, diese Hürden zu überwinden.

 

Boo

Much better

Und zündende Ideen gab‘ es bereits! Ein Beispiel: Google Chrome baut sich aus HTML-Code die PDFs zusammen, lässt sich es aber nicht nehmen, automatisch generierte Header und Footer anzufügen, mit Seitenzahl und Datum. Das ist natürlich ein Element, welches wir gerne unter Kontrolle hätten! Der erste Hack, mit dem ich mich über diese Hürde bewegt habe, ist ein CSS-Styleelement an den übergeben HTML-Code anzufügen. Das ist in seinem momentanen Zustand natürlich noch etwas unschön gelöst, wenn einfach nur der HTML-Code um ein fest eingebautes Codeelement erweitert wird, aber wir haben einen Einstiegspunkt, einen Ansatz, mit der wir weiterbauen und -basteln können.

 

Scheue Dich vor Fragen nicht!

Auch wenn der Kerngedanke dieses Projekts natürlich daraus besteht, dass wir uns selbst beweisen, spricht natürlich Nichts dagegen, bei Fragen, die aufkommen, einen der zahlreichen Experten die NETWAYS hat zu involvieren. Was Benutzeroberflächen angeht, ist bei uns UX-Designer Florian der Ansprechpartner Nummer 1. Wir haben uns zusammengesetzt und konnten ihm live beim Bauen eines Mockups in Sketch zusehen. Eine Gelegenheit, durch die großen Augen, die wir gemacht haben, auch etwas über grundlegende Designansätze in Icingaweb zu lernen, und Grundsätze bezüglich Design. Mit diesem Wissen können wir erstmal diesen Pfad weiter selbst beschreiten, aber natürlich werden wir viel Rücksprache führen. Nur weil man bisher nicht dazu kam, sich extensive Kenntnisse in einem Bereich selbst anzueignen, heißt das nicht, dass man dieses Wissen, wenn es einem zur Verfügung steht, nicht nutzen kann. Und immer schön dran denken, selbst den Fundus an Talenten zu erweitern.

A mockup of things to come…

 

Kooperatives Kodieren

Einer der in meiner Einleitung angesprochenen Tricks, den ich einfach für neat halte, und sehr dem Spirit dieses Projektes entspricht, ist die Möglichkeit in git Co-Authoren einzutragen. Dafür muss man in der commit message einfach

 Co-authored-by: Name <e-mail>

angeben, und git verlinkt automatisch zwei Autoren für diesen commit. In diesem Fall meine beiden Kollegen bei diesem Projekt. Gute Arbeit, Jungs!

Da kann man ruhig mal klatschen! ?

 

Willst Du uns vielleicht nächstes Jahr bei dem nächsten Entwicklungsprojekt behilflich sein? Dann raus mit Deiner Bewerbung an NETWAYS! Wir freuen uns schon auf dich!