-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.html
1034 lines (876 loc) · 46.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Foreign Data Wrapper sous PostgreSQL</title>
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/white.css">
<link rel="stylesheet" href="css/custom.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="lib/css/idea.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section id="title">
<h1>Foreign Data Wrapper</h1>
<h2>ou un réseau social pour vos données</h2>
<h2>sous PostgreSQL</h2>
</section>
<section id="qui-suis-je">
<h1>Qui suis-je ?</h1>
<div class="flex-row">
<div>
<img id="avatar" class="clean-image" src="./assets/img/my-disk-world.png" alt="avatar" />
</div>
<div class="flex-element-centered">
<div class="name">Florian FERRIERE</div>
<div class="twitter">
<a href="https://twitter.com/FerriereFlorian">@FerriereFlorian</a>
</div>
</div>
</div>
<div>
<div>
Développeur chez Norsys
</div>
<div>
<img id="logo-norsys" src="./assets/img/norsys.png" alt="norsys" />
</div>
</div>
</section>
<section id="pg_en_3_mots">
<section id="postgresql">
<div>
<div>
<img id="logo-postgresql" class="clean-image" src="./assets/img/postgresql.png" alt="postgresql" />
</div>
<h1>
PostgreSQL
</h1>
<aside class="notes">
Avant de parler des Foreign Data Wrapper,
je vais rapidement vous parler des PostgreSQL.
</aside>
</div>
</section>
<section id="historique">
<h1>Historique</h1>
<p class="fragment">1974 : Création d'Ingres</p>
<p class="fragment">1985 : Refonte de zéro d'Ingres pour créer <i>postgres</i></p>
<p class="fragment">1995 : Ajout des fonctionnalités SQL => Postgres 95</p>
<p class="fragment">1996 : Renommage en PostgreSQL</p>
<p class="fragment">2016 : Version 9.6</p>
<aside class="notes">
Bref historique :
<ul>
<li>PostgreSQL est une base de données dont les origines commencent en 1974
avec la création d'Ingres par Michael Stonebraker.</li>
<li>En 1985, il entamme une refonte à partir de zéro d'Ingres, intitulé postgres,
qui est le diminutif de "post-Ingres".</li>
<li>Un tournant important arrive en 1995 avec l'ajout des fonctionnalités liés au SQL.
Le projet fut alors renommé "Postgres 95"</li>
<li>1 an plus tard, il devient alors "PostgreSQL".</li>
<li>Beaucoup de travaux plus tard ont pu donner la version 9.6 dernière version stable à ce jour.</li>
</ul>
</aside>
</section>
<section id="fonctionnalites_generales">
<h1>Fonctionnalités générales</h1>
<ul>
<li class="fragment">SGBDRO</li>
<li class="fragment">Accessible : JDBC, pgsql, ...</li>
<li class="fragment">
SQL :
<ul>
<li class="fragment">Triggers</li>
<li class="fragment">Common Table Expressions</li>
<li class="fragment">Procedural Language (PL/pgSQL, PL/JAVA, PL/PHP, ...)</li>
<li class="fragment">Vues et Vues matérialisées</li>
<li class="fragment">...</li>
</ul>
</li>
<li class="fragment">JSON</li>
<li class="fragment">PostGIS</li>
<li class="fragment">...</li>
<li class="fragment">Foreign Data Wrapper</li>
</ul>
<aside class="notes">
Fonctionnalités générales :
<ul>
<li>Server de Gestion de Base de Données Relationnel Objet</li>
<li>
Accessible avec plein de language de programation :
- JDBC pour JAVA
- lib pgsql pour PHP
</li>
<li>
SQL :
- PL : PL/pgSQL, PL/Tcl, PL/Perl et PL/Python de base
- <a href="https://www.postgresql.org/docs/9.6/static/external-pl.html">Extension pour PL</a> : PL/JAVA, PL/PHP, PL/Py, PL/R, PL/Ruby
</li>
<li>
PostgreSQL permet de stocker et de manipuler du JSON.
</li>
<li>
PostGIS : gestion des objets Spatiaux et Géographique.
Utilisé par OpenStreetMap.
</li>
<li>...</li>
<li>Foreign Data Wrapper</li>
</ul>
</aside>
</section>
</section>
<section id="foreign_data_wrapper">
<section id="fdw_explication">
<h1>Foreign Data Wrapper</h1>
<h2>Qu'est-ce que c'est ?</h2>
<p class="fragment">Norme SQL/MED (Management of External Data)</p>
<p class="fragment">Créée en 2003</p>
<p id="creation_wrapper" class="fragment">
Création de wrapper :
<ul>
<li class="fragment">Nativement en C</li>
<li class="fragment">En python avec "Multicorn"</li>
<li class="fragment">En ruby avec "Holycorn"</li>
</ul>
</p>
<aside class="notes">
Le Foreign Data Wrapper est une implémentation de la norme SQL/MED
(Management of External Data), créée en 2003.
Comme son nom l'indique, cette norme permet la gestion des données externes,
depuis un autre SGBD ou des sources diverses.
<br />
Il est possible de créer son propre wrapper en C en utilisant
les fonctions prévues à cet effet.
<br />
Des extensions comme "Multicorn" en python ou "Holycorn" en ruby
apporte un lot de wrapper et permet de créer des wrappers plus facilement.
Grace à ça, il existe, à ce jour, un nombre important de wrapper.
</aside>
</section>
<section id="wrappers_list">
<h1>Quelques Wrappers</h1>
<div id="SGDB_fdw">
<p class="fragment">PostgreSQL</p>
<p class="fragment">SGBD SQL courants : MySQL, Oracle, SQL Server, SQLite, ...</p>
<p class="fragment">Connecteurs SQL génériques : ODBC, JDBC</p>
<p class="fragment">NoSQL : MongoDB, Cassandra, Redis, ...</p>
</div>
<div id="extra_fdw">
<p class="fragment">CSV, XML, LDAP</p>
<p class="fragment">Git, Logs, WWW, IMAP</p>
<p class="fragment">Processus, Twitter, Docker, RabbitMQ, ...</p>
</div>
<aside class="notes">
Voici une liste non exhaustive :
<ul>
<li>Forcément nous avons la possibilité de connecté un serveur PostgreSQL avec un autre.</li>
<li>Mais également avec d'autres SGBD.</li>
<li>Soit avec les connecteurs natifs</li>
<li>Soit avec des connecteurs génériques</li>
<li>Il est aussi possible de dialoguer avec des serveurs NoSQL.</li>
<li>Il y a aussi des connecteurs vers des fichiers CSV, XML ou un serveur LDAP</li>
<li>Ou encore des systèmes plus atypiques comme Git, les logs systèmes, des services web ou des mails via le protocol IMAP</li>
<li>Et pourquoi pas la liste des processus du système, votre timeline twitter</li>
<li>Et même une connexion avec le serveur docker pour lister les containeurs ou les images</li>
<li>
Très récemment un wrapper pour RabbitMQ,
c'est du coup très prometteur
pour mettre en place de l'event-sourcing.
</li>
<li>Et tant d'autre</li>
</ul>
</aside>
</section>
<section id="evolution1">
<h1>Évolution (1/2)</h1>
<div>
<p class="fragment"><strong>07/2009 : v8.4</strong> => Démarrage de l'implémentation du SQL/MED</p>
<p class="fragment"><strong>09/2011 : v9.1</strong> => Arrivée des "Foreign Tables"</p>
<p class="fragment">
<strong>09/2013 : v9.3</strong><br />
Ajout de l'écriture sur les Foreign Tables
</p>
<p class="fragment">
<strong>10/2014 : v9.4</strong><br />
Ajout des triggers sur les Foreign Tables
</p>
</div>
<aside class="notes">
<ul>
<li>C'est en Juillet 2009 que nous avons pu voir arriver l'implémentation du SQL/MED. Les prémices du FDW ont commencés</li>
<li>Septembre 2011 sont arrivés les Foreign Tables. Fondement pour l'utilisation de SGBD distant.</li>
<li>Depuis Septembre 2013 nous avons la possibilité d'écrire sur les tables distantes.</li>
<li>En Octobre 2014, les triggers sur les tables distantes ont été ajoutés au panel des fonctionnalités.</li>
</ul>
</aside>
</section>
<section id="evolution2">
<h1>Évolution (2/2)</h1>
<div>
<p class="fragment">
<strong>01/2016 : v9.5</strong><br />
IMPORT FOREIGN SCHEMA<br />
<i>INSERT ... ON CONFLICT DO NOTHING<span class="fragment highlight-line-through">/UPDATE</span></i>
</p>
<p class="fragment">
<strong>09/2016 : v9.6</strong><br />
JOIN et SORT directement sur le serveur distant<br />
De même pour les UPDATE et les DELETE<br />
Exécution d'opérateurs et de fonction sur le serveur distant<br />
Possibilité d'annuler une requête distante
</p>
</div>
<aside class=notes>
<ul>
<li>
De puis Janvier de cette année il est possible d'importé un schema distant plutôt que de créer les différentes tables distantes.
La version 9.5 apporte aussi la commande INSERT ... ON CONFLICT DO NOTHING ou UPDATE.
Cette commande permet d'éviter les erreurs en cas d'insertion d'une ligne créant un doublon sur un champ unique.
Il est possible de ne rien faire ou d'éxécuter une commande UPDATE.
La commande est aussi disponible sur les tables distantes mais uniquement avec le NOTHING.
</li>
<li>
La dernière version est sortie en septembre 2016, elle apporte (<a href="https://wiki.postgresql.org/wiki/NewIn96#postgres_fdw" target="_blank">NewIn96</a>) :
<ul>
<li>une amélioration pour faire le JOIN et le SORT directement sur le serveur distant</li>
<li>l'amélioration de la gestion des UPDATE et les DELETE à distance (<a href="https://www.depesz.com/2016/03/22/waiting-for-9-6-directly-modify-foreign-tables/" target="_blank">lien</a>)</li>
<li>possibilité d'éxécuter, côté serveur distant, des opérateurs et fonctions (<a href="http://paquier.xyz/postgresql-2/postgres-9-6-feature-highlight-pushdown-improvements-postgres-fdw/" target="_blank">lien</a>)</li>
<li>la possibilité d'annuler une requête distante</li>
</ul>
</li>
</ul>
</aside>
</section>
</section>
<section id="cas_utilisation">
<h1>Cas d'utilisation</h1>
<section id="vide">
</section>
<section id="client-server">
<h2>Client/Server</h2>
<img id="pic-databases" class="clean-image" src="assets/img/databases.png" alt="databases" />
<aside class="notes">
Voici un cas d'utilisation qui m'est venu en tête et que j'ai pu mettre en pratique,
est une utilisation décentraliser. Nous avons deux serveurs de bases de données.
Le premier contient la totalité des données.
Le second tout ou partie de ces données pour les rendre accessible à un serveur web.
</aside>
</section>
</section>
<section id="comment_ca_marche">
<h1>Comment ça marche ?</h1>
<section>
</section>
<section id="declaration_connexion">
<h2>Déclaration de l'extension</h2>
<pre><code class="hlsql">
CREATE EXTENSION postgres_fdw;
</code></pre>
<aside class="notes">
On déclare l'extension pour pouvoir l'utiliser dans PostgreSQL.
/!\ Nécessite certains droits utilisateurs.
</aside>
</section>
<section id="creation_connexion">
<h2>Déclaration du serveur distant</h2>
<pre><code class="hlsql">
CREATE SERVER nom_remote
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
host 'remote.url.lan',
dbname 'ma_db'
);
</code></pre>
<aside class="notes">
On créer le serveur distant.
D'autres options comme le port de connexion existent.
</aside>
</section>
<section id="mapping_utilisateur">
<h2>Mapping utilisateur</h2>
<pre><code class="hlsql">
CREATE USER MAPPING FOR mon_local_user
SERVER mon_remote
OPTIONS (
user 'mon_remote_user',
password 'mon_remote_passwd'
);
</code></pre>
<aside class="notes">
Les comptes utilisateurs ne sont pas forcément les mêmes
entre les deux instances.
Nous devons donc mapper notre utisateur local avec l'utilisateur distant.
</aside>
</section>
<section id="creation_table_distante">
<h2>Création d'une table distante</h2>
<pre><code class="hlsql">
CREATE FOREIGN TABLE table_locale_mais_distante (
id INTEGER,
col1 VARCHAR(32),
col2 BOOLEAN
)
SERVER mon_remote
OPTIONS (
table_name 'le_nom_de_ma_table',
updatable 'false'
);
</code></pre>
<aside class="notes">
La création d'une table étrangère se fait presque comme une table normale.
En plus du mot clef "FOREIGN"
nous devons spécifié le serveur sur lequelle est la table ainsi que quelques options.
Par défaut les tables distantes sont modifiables et le schema est 'public'.
</aside>
</section>
<section id="utilisation_table_distante">
<h2>Utilisation</h2>
<pre><code class="hlsql">
SELECT id, col1
FROM table_locale_mais_distante
WHERE col2 IS TRUE
ORDER BY col1;
</code></pre>
<aside class="notes">
Une fois la table correctement créée,
l'utilisation est la même qu'avec une table standard.
</aside>
</section>
<section id="encore_plus_facile">
<h2>Encore plus facile</h2>
<pre><code class="hsql">
IMPORT FOREIGN SCHEMA schema_distant
FROM SERVER mon_remote
INTO mon_schema_local;
</code></pre>
<aside class="notes">
Depuis la version 9.5 de PostgreSQL,
nous avons la possibilité d'utiliser la commande
IMPORT FOREIGN SCHEMA qui va permettre
de récupérer les informations des tables distantes
et les transposer sur l'instance locale.
</aside>
</section>
</section>
<section id="exemple">
<h1>Exemple</h1>
<section id="exemple_presentation">
<p id="project-link">
<a href="https://github.com/fferriere/presentation_pg_fdw">Projet Github</a>
</p>
<img id="pic-databases-exemple" class="clean-image" src="assets/img/databases.png" alt="databases" />
<aside class="notes">
J'ai créer un projet avec 2 conteneurs docker
pour montrer l'utilisation des foreign data wrapper.
Le README.md explique comment l'utiliser.
<br />
Les exemples suivants sont tirés de ce projet.
</aside>
</section>
<section id="tables_server">
<h2>Tables sur <i>server</i></h2>
<pre class="result_sql">
server=# \d
Liste des relations
Schéma | Nom | Type | Propriétaire
--------+-----------------+----------+--------------
<strong>public | country | table | user</strong>
public | country_id_seq | séquence | user
<strong>public | customer | table | user</strong>
public | customer_id_seq | séquence | user
(4 lignes)
</pre>
<aside class="notes">
Voici la liste des tables sur le serveur.
Nous avons une table country et une autre customer
ainsi que les sequence associée.
</aside>
</section>
<section id="description_table_server">
<h2>Table sur le <i>server</i></h2>
<pre class="result_sql">
server=# \d country
<strong>Table « public.country »</strong>
Colonne | Type | Modificateurs
---------+-----------------------+---------------
<strong>id</strong> | integer | non NULL
<strong>alpha2</strong> | character(2) | non NULL
<strong>alpha3</strong> | character(3) | non NULL
<strong>label</strong> | character varying(64) | non NULL
Index :
"country_pkey" PRIMARY KEY, btree (id)
</pre>
</section>
<section id="select_table_server">
<h2>Table sur le <i>server</i></h2>
<pre class="result_sql">
server=# <strong>SELECT count(id) FROM country;</strong>
count
-------
<strong class="result">241</strong>
(1 ligne)
server=# <strong>SELECT id, alpha2, alpha3, label
FROM public.country
WHERE alpha2 = 'FR';</strong>
id | alpha2 | alpha3 | label
----+--------+--------+--------
<strong class="result">75 | FR | FRA | France</strong>
(1 ligne)
</pre>
<aside class="notes">
Nous pouvons voir qu'il y a des données.
</aside>
</section>
<section id="tables_client">
<h2>Tables sur le <i>client</i></h2>
<pre class="result_sql">
client=# \d
Liste des relations
Schéma | Nom | Type | Propriétaire
--------+------------+------------------+--------------
<strong>public | f_country | table distante | user</strong>
<strong>public | f_customer | table distante | user</strong>
<strong class="result">public | m_customer | vue matérialisée | user</strong>
<strong class="result">public | t_customer | vue matérialisée | user</strong>
<strong>public | u_country | table distante | user</strong>
(5 lignes)
</code></pre>
<aside class="notes">
Ici nous avons listé les tables sur le client.
J'ai pour habitude de préfixer les tables étrangères par un f.
Pour l'exemple j'ai créer la table u_country,
c'est la même table que f_country mais updatable.
Nous pouvons voir qu'il y a une vue matériliasée.
Nous reviendrons dessus.
</aside>
</section>
<section id="description_table_client">
<h2>Table sur le <i>client</i></h2>
<pre class="result_sql">
client=# \d f_country
<strong>Table distante « public.f_country »</strong>
Colonne | Type | Modificateurs | Options FDW
---------+-----------------------+---------------+-------------
<strong>id</strong> | integer | |
<strong>alpha2</strong> | character(2) | |
<strong>alpha3</strong> | character(3) | |
<strong>label</strong> | character varying(64) | |
Server: <strong class="result">server</strong>
FDW Options: (
<strong class="result">schema_name 'public',
table_name 'country',
updatable 'false'</strong>)
</code></pre>
<aside class="notes">
Nous avons une description de la table f_country,
à l'image de la table sur server.
</aside>
</section>
<section id="select_table_client">
<h2>Table sur le <i>client</i></h2>
<pre class="result_sql">
client=# <strong>SELECT count(id) FROM f_country;</strong>
count
-------
<strong class="result">241</strong>
(1 ligne)
client=# <strong>SELECT id, alpha2, alpha3, label
FROM public.f_country
WHERE alpha2 = 'FR';</strong>
id | alpha2 | alpha3 | label
----+--------+--------+--------
<strong class="result">75 | FR | FRA | France</strong>
(1 ligne)
</pre>
<aside class="notes">
Nous pouvons voir qu'il y a les même données.
</aside>
</section>
</section>
<section id="vue_materialisee">
<h1>Vue matérialisée</h1>
<section id="creation_vue_materialisee">
<p class="fragment">Mélange entre une vue et une table</p>
<pre class="fragment"><code class="hlsql">
CREATE MATERIALIZED VIEW m_customer AS
SELECT id, lastname, firstname,
date_part('year', age(birthdate)) as age
FROM f_customer
WITH NO DATA;
CREATE INDEX m_customer_keys
ON m_customer (id);
</code></pre>
<pre class="fragment"><code class="hlsql">
REFRESH MATERIALIZED VIEW m_customer;
</code></pre>
<aside class="notes">
Nous avons, ici, la requête de création de vue matérialisées.
Une vue matérialisées est entre une table et une vue.
La construction ce fait, comme une vue, à partir d'une requête.
Par contre les données sont stockées.
Ce qui fait que l'on peut ajouter des index.
Le fait d'avoir une construction par requête permet
de spécialiser les informations pour notre besoin.
<br />
L'option 'WITH NO DATA' est facultative,
elle permet de créer une vue sans charger les données.
<br />
Le rafraichissement des données d'une vue matérialisée
se fait, simplement, avec la commande 'REFRESH MATERIALIZED VIEW'.
</aside>
</section>
<section id="utilisation_vue_materialisee">
<pre class="result_sql">
server=# <strong>SELECT id, lastname, firstname, birthdate
FROM customer LIMIT 2;</strong>
id | lastname | firstname | <span class="result">birthdate</span>
----+----------+-----------+------------
<strong class="result">1 | Potter | Harry | <span class="result">1980-08-31</span>
2 | Weasley | Ron | <span class="result">1980-03-01</span></strong>
(2 lignes)
client=# <strong>SELECT id, lastname, firstname, age
FROM m_customer LIMIT 2;</strong>
id | lastname | firstname | <span class="result">age</span>
----+----------+-----------+-----
<strong class="result">1 | Potter | Harry | <span class="result">36</span>
2 | Weasley | Ron | <span class="result">36</span></strong>
(2 lignes)
</code></pre>
<aside class="notes">
Nous pouvons voir que la vue matérialisée
retourne bien des données différentes
que la table initiale.
</aside>
</section>
</section>
<section id="cas_pratique">
<h1>Cas pratique</h1>
<section id="besoin">
<h2>Besoin</h2>
<p class="fragment">Application Web</p>
<p class="fragment">IBM AS400/DB2</p>
<p class="fragment">
<span class="fragment highlight-line-through">Driver PHP</span>
</p>
<p class="fragment">JDBC</p>
<p class="fragment">jdbc2_fdw</p>
<aside class="notes">
Je vais maintenant vous parler du cas pratique que j'ai pu mettre en place.
Voici une étude du besoin.
<br />
Le besoin était d'avoir une application web,
permettant à nos clients d'accéder à leurs informations.
<br />
Les données sont stockées sur un IBM/AS400 qui intègre un base DB2.
Premier problème le driver PHP-DB2 n'est pas facilement accessible, en tout cas sous Linux.
OS de notre hébergement.
J'ai réussi, tant bien que mal à le compiler, mais une fois fais j'avais une erreur de license.
Bienvenue dans le monde merveilleux d'IBM.
<br />
J'avais pu remarquer qu'avec un outils de DB SQL générique développé en Java (DBeaver),
j'avais un connecteur JDBC pour DB2.
Après configuration j'ai pu facilement accéder aux données sur le serveur.
<br />
C'est là que j'ai découvert les FDW et le <a href="https://github.com/heimir-sverrisson/jdbc2_fdw/">jdbc2_fdw</a>.
Après quelques essais plutôt concluant je me suis lancé.
</aside>
</section>
<section id="explication_cas_pratique">
<h2>Explication</h2>
<img class="clean-image" src="assets/img/as400.png" alt="as_400_proxy_web" />
<aside class="notes">
Nous avons donc les données qui sont stockées sur l'AS400.
<br />
Nous avons aussi le serveur "proxy", va lui se connecter, via jdbc2_fdw, à l'AS400.
Ce serveur ne sert que de passe-plat entre l'AS400 et le serveur pour le Web.
Il permet de faire quelques conversions de données :
les dates n'étaient pas stockées dans un format de date sur l'AS400
ou les booléens étaient stockées comme des entiers, ...
<br />
Enfin nous avons la base de données pour l'appli Web.
Cette base de données va stocker des données qui lui sont propres.
Elle va aussi se connecter au serveur PostgreSQL nommé "proxy".
</aside>
</section>
<section id="rafraichissement_proxy">
<h2>Rafraichissement du proxy</h2>
<pre class="fragment"><code class="hlsql">
CREATE FOREIGN TABLE f_ma_table (...) SERVER server OPTIONS (...);
CREATE MATERIALIZED VIEW m_ma_table AS
SELECT ... FROM f_ma_table;
CREATE FUNCTION refresh_view(name) RETURNS INTEGER AS $$
BEGIN
EXECUTE format('REFRESH MATERIALIZED VIEW %s', name);
RETURN 1;
END;
$$ LANGUAGE 'plpgsql';
CREATE VIEW refresh_ma_table AS SELECT refresh_view('m_ma_table');
</code></pre>
<aside class="notes">
Nous avons besoin que nos données restent à jour.
Nous avons vu que mettre à jour une vue matérialisée est très simple.
Il suffit d'appeler la commande REFRESH MATERIALIZED VIEW.
<br />
Comme nous avions quelques conversions entre l'AS400 et le "proxy",
et pour éviter de garder la connexion avec l'AS400 trop longtemps,
les données sont enregistrées dans des vues matérialisés.
Cela nous permet aussi d'avoir des index personnalisés et
d'améliorer les performances.
<br />
J'ai mis en place l'appel du "refresh" au travers d'une fonction,
appelée dans une vue.
Cette vue pouvait être concidérée comme une table distante
sur l'instance web.
Avec cette technique, il est possible de gérer le rafraichissement
des données, depuis l'instance web.
</aside>
</section>
</section>
<section>
<section>
<h1>Problème 1</h1>
<p>Comment rafraichir mes données sans bloquer mon application ?</p>
<aside class="notes">
Le rafraichissement d'une vue matérialisée verrouille son accès en lecture.
Si un visiteur accède à une page web au même moment que le rafraichissement,
le chargement de la page va être ralenti, peut être même fortement.
</aside>
</section>
<section id="double_materialized_views">
<h1>Solution 1</h1>
<p class="fragment">Deux vues matérialisées</p>
<p class="fragment">La première sur la table distante</p>
<p class="fragment">La seconde sur la première vue mat</p>
<aside class="notes">
Pour réduire se temps de verrouillage,
j'ai mis en place deux vues matérialisées.
La première vue récupère les données de la tables distante.
La seconde vue récupère les données de la première vue.
</aside>
</section>
<section id="double_materialized_views_graph">
<h1>Solution 1</h1>
<img id="double_materialized_views" class="clean-image" alt="graph" src="./assets/img/refresh_double_views.png" />
</section>
<section id="double_materialized_views_query">
<h1>Solution 1</h1>
<pre><code class="hlsql">
CREATE MATERIALIZED VIEW t_customer AS
SELECT * FROM m_customer
WITH NO DATA;
CREATE INDEX t_customer_keys
ON t_customer (id);
</code></pre>
</section>
</section>
<section>
<section>
<h1>Problème 2</h1>
<p>Comment rafraichir régulièrement des données très volumineuses ?</p>
<aside class="notes">
Il est possible que nous ayons besoin de récupérer
des tables très volumineuse,
et de vouloir parcourir toutes ces données.
<br />
Le problème est que le rappatriement des données peux prendre très longtemps.
Exemple : il est compliqué d'avoir des données à jour toutes les 10 minutes
si elles mettent 20 minutes à être récupérées.
</aside>
</section>
<section>
<h1>Solution 2</h1>
<p class="fragment">Découpage en 2 vues :</p>
<p class="fragment">Une première qui contient toutes les données</p>
<p class="fragment">Une seconde qui contient qu'un fragment des données</p>
<p id="troisieme_vue" class="fragment">Une 3ème vue qui JOIN le tout</p>
<aside class="notes">
</aside>
</section>
<section>
<h1>Solution 2</h1>
<img id="pic-big-data" class="clean-image" alt="big_data" src="./assets/img/big_data.png" />
</section>
</section>
<section id="git_fdw">
<h1>Git FDW</h1>
<section>
</section>
<section id="git_fdw_presentation">
<h2>Déclaration du serveur</h2>
<pre><code class="hlsql">
CREATE EXTENSION multicorn;
CREATE SERVER git_server
FOREIGN DATA WRAPPER multicorn
OPTIONS (
wrapper 'multicorn.gitfdw.GitFdw'
);
</code></pre>
<aside class="notes">
Nous allons voir rapidement comment utiliser le FDW pour git.
Il y a deux extensions pour GIT une en C et un composant compris dans multicorn.
<br />
J'aurai préféré utiliser l'extension en C
mais la compilation n'a pas fonctionnée.
<br />
Je me suis donc rabattu sur l'extension multicorn.
Sont installation est décrite dans le <a href="../../../git_fdw/README.md" target="_blank">README</a> que j'ai utilisé pour tester.
<br />
Une fois l'installation faite, la configuration est très simple.
Comme on l'a déjà vu tout à l'heure : on crée l'extension.
Puis le serveur.
</aside>
</section>
<section>
<h2>Déclaration de la table</h2>
<pre><code class="hlsql">
CREATE FOREIGN TABLE git_atoum (
author_name VARCHAR(128),
author_email VARCHAR(128),
message TEXT,
hash VARCHAR(128),
date TIMESTAMP
)
SERVER git_server
OPTIONS (
path '/opt/atoum'
);
</code></pre>
<aside class="notes">
Ensuite il suffit de créer la table distante avec les bonnes options.
</aside>
</section>
<section id="git_fdw_select">
<h2>SELECT</h2>
<pre><code class="hlsql">
SELECT to_char(date, 'YYYY-TQ') AS quarter,
COUNT(*) as nb_commit
FROM git_atoum
GROUP BY to_char(date, 'YYYY-TQ')
ORDER BY quarter ASC;
</code></pre>
<aside class="notes">
Voici un exemple de requête SQL que nous pouvons faire.
</aside>
</section>
<section id="git_fdw_result">
<h2>Résultat</h2>
<pre> quarter | nb_commit
---------+-----------
2010-T2 | 25
2010-T3 | 64
2010-T4 | 108
2011-T1 | 144
2011-T2 | 199
2011-T3 | 230
2011-T4 | 174
2012-T1 | 89
2012-T2 | 137
2012-T3 | 150
2012-T4 | 184
2013-T1 | 215
2013-T2 | 234
2013-T3 | 119
2013-T4 | 217</pre>
<pre> quarter | nb_commit
---------+-----------
2014-T1 | 108
2014-T2 | 29
2014-T3 | 26
2014-T4 | 110
2015-T1 | 58
2015-T2 | 46
2015-T3 | 65
2015-T4 | 75
2016-T1 | 85
2016-T2 | 27
2016-T3 | 43
2016-T4 | 7
(27 rows)
Time: 63.959 ms</pre>
<aside class="notes">
Le résultat est rapide et simple à optenir.
Ce résultat serai bien plus compliqué à obtenir en ligne de commande, mais sûrement pas impossible.
</aside>
</section>
<section id="git_fdw_select_in_json">
<h2>Graph (requête)</h2>
<pre><code class="hlsql">
SELECT to_json(t)
FROM (
SELECT array_agg(data.quarter) AS labels,
array_agg(data.nb_commit) AS data
FROM (
SELECT to_char(date, 'YYYY-TQ') AS quarter,
COUNT(*) as nb_commit
FROM git_atoum
GROUP BY to_char(date, 'YYYY-TQ')
ORDER BY quarter ASC
) AS data
) AS t;
</code></pre>
<aside class="notes">
En associant la requête précédente avec le module JSON de PostgreSQL,
nous pouvons obtenir un résultat en JSON utilisable directement (ou presque).
</aside>
</section>
<section id="git_fdw_chart">
<h2>Graph (resultat)</h2>
<iframe src="./git-fdw.chart.html" style="width: 100%; min-height: 450px;" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
<aside class="notes">
Voici comment on peut utiliser le résultat en JSON assez facilement pour avoir un graph.
</aside>
</section>
</section>
<section id="documentation">
<h1>Documentation</h1>
<p>
<a href="https://wiki.postgresql.org/wiki/Foreign_data_wrappers">Page des Foreign Data Wrappers</a>
</p>
<p>
<a href="https://www.postgresql.org/docs/">Documentation</a>
</p>
<p>
<a href="https://www.youtube.com/watch?v=iOwnUPSY0KI">Postgresql : la nouvelle base orientée document par Yan Bonnel (Vidéos)</a>
</p>
<p>
<a href="https://github.com/fferriere/presentation_pg_fdw">Projet Github</a>