SGBDR et immuabilité : prenons des leçons de git

2 views
Skip to first unread message

Laurent Caillette

unread,
Jul 22, 2008, 9:41:01 AM7/22/08
to tec...@googlegroups.com
Salut,

Je viens de me taper l'excellent "Java persistence with Hibernate"
(Bauer, King) qui permet d'embrasser toute la puissance d'Hibernate.
C'est un peu fouillis mais l'information est là, sans délayage, avec
une vision très claire de l'intégration des différentes couches
applicatives. Bon bouquin, donc.

Pourtant je n'arrive pas à me défaire de l'impression qu'Hibernate
repose sur une erreur fondamentale : des effets de bord, qui induisent
une complexité que le produit s'attache à régler partiellement,
donnant cette impression de puissance toujours à la limite de la perte
de contrôle. On a des objets qui représentent des choses complètement
fugaces, comme l'état de la base de donnée, puis rien du tout dès lors
qu'ils sont "détachés" de la session. On peut ensuite les rattacher,
cooool ! Evidemment tout ce genre d'acrobaties génère de la prestation
et l'impression d'utiliser "what's between your ears" (formule de
Goldratt).

On peut dire que c'est la faute de la JVM qui ne permet pas de
détruire des objets explicitement. Ou que c'est la faute des SGBDR
dont la raison d'être est de capturer un état muable. Oh mais
blasphème que tout cela, les SGBDR sont teeellement irremplaçables...

Offrons-nous mainenant un petit détour par git, le gestionnaire de
version distribué qui :
# Met tous ses concurrents à plat en termes de fonctionnalités.
# Est bâti sur des structures de données immuables.

C'est de cette dernière particularité architecturale que découlent
toutes ses fonctionnalités. Les deltas se prêtent à toutes sortes de
manipulations hors de la structure rigide d'un référentiel
(littéralement les empiler dans un coin avec git-stash). Il sont
signés cryptographiquement, ce qui sécurise les échange sur le réseau
et permet de récupérer du code sur un repo inconnu sans qu'une
altération passe inaperçue. Pour la synchro des repos il suffit de
savoir à quelle version s'est arrêté le repo esclave : il est
structurellement garanti que la base commune est la même.

Ainsi d'une seule propriété architecturale, les données immuables,
découlent assez de simplifications pour permettre un produit induisant
une rupture qualitative radicale. Imaginons maintenant un SGBDR qui
applique les formules de git. Je parle bien d'une approche
relationnelle. On voit fleurir sur le marché différentes solutions de
stockage pour des volumes colossaux (`>= Teraoctet`) : BigTable,
Mnesia... Mais la sémantique relationnelle a quand même de beaux jours
devant elle, notamment parce qu'elle donne aux pointeurs une forme
aisément compréhensible.


Concrètement, ça ressemblerait à quoi ? Disons que nous avons une
table A qui contient des références vers une table B :

Version 1
¨¨¨¨¨¨¨¨¨
| A | | B |
|----------------------| |---------------|
| A_ID | B_ID | A_NAME | | B_ID | B_NAME |
|------|------|--------| |------|--------|
| 10 | 20 | aah | | 20 | bar |


(Comme d'habitude, ce sera plus clair avec une police
non-proportionnelle. L'historique de Google Groups permet ça.)


Appliquons un changement sur NAME dans la ligne 10 de A. Un SGBDR basé
sur des données muables représenterait les données comme ceci :

Version 2
¨¨¨¨¨¨¨¨¨
| A | | B |
|--------------------| |---------------|
| A_ID | B_ID | NAME | | B_ID | B_NAME |
|------|------|------| |------|--------|
| 10 | 20 | ooh | | 20 | bar |


Mais si on veut que la version 0 existe toujours il faut ajouter le
numéro de version. Disons qu'il apparaît dans une colonne sur la
gauche de chaque table et indique pour chaque ligne la version de la
base dans laquelle apparaît la ligne dans son état. Le numéro de
version au niveau du nom de la table indique la version dans laquelle
la table a été modifiée pour la dernière fois. On a maintenant :

Version 2
¨¨¨¨¨¨¨¨¨
| 2 | A | | 1 | B |
|---|--------------------| |---|------|--------|
| | A_ID | B_ID | NAME | | | B_ID | B_NAME |
|---|------|------|------| |---|------|--------|
| 1 | 10 | 20 | aah | | 1 | 20 | bar |
| 2 | 10 | 20 | ooh |


Que se passe-t-il si on modifie la ligne de B référencée par A ?
Normalement on retombe sur nos pattes parce que si on souhaite revenir
en version 2, comme il n'y a pas de version 2 dans B, c'est parce
qu'au moment où la base était dans sa version 2 la version de B
correspond à son plus haut numéro de version strictement inférieur à
3. Voilà ce qu'on aurait après modification de B :

Version 3
¨¨¨¨¨¨¨¨¨
| 2 | A | | 3 | B |
|---|--------------------| |---|------|--------|
| | A_ID | B_ID | NAME | | | B_ID | B_NAME |
|---|------|------|------| |---|------|--------|
| 1 | 10 | 20 | aah | | 1 | 20 | bar |
| 2 | 10 | 20 | ooh | | 3 | 20 | baz |


C'est bien joli, on voit s'aligner les versions, mais que se
passe-t-il si on a plusieurs lignes et qu'on change les clés primaires
? Réponse : on ne peut pas !C'est généralement considéré comme une
mauvaise pratique, le support de cette fonctionnalité doit ajouter
beaucoup de code bizarre, donc on dégage et la corrélation se fait
toute seule.

A l'évidence, on gagne :

* Une isolation parfaite des transactions.

* Utiliser un même état de référence à travers plusieurs transactions.
Hibernate s'approche de cela avec le flush "en fin de conversation"
sans y arriver parfaitement.

* Une piste d'audit dont l'intégrité est garantie par la base
elle-même. Un produit comme Envers se contente d'alimenter des tables.

D'une façon moins évidente (mes connaissances restreintes en SGBDR
m'obligent à me parer de ces précautions oratoires) :

* Des branches, comme dans git. Si on sait gérer les versions, l'ajout
des branches ne nécessite pas un travail démesuré (voir comment fait
git). On peut alors entasser des modifications dans une branche, et
regarder ce que donnerait un merge. Il y a même là l'opportunité de
redéfinir la notion de transaction : on crée une branche, quand on a
mis toutes les données souhaitées on essaye de la promouvoir en tant
que dernière version. Le MultiVersion Concurrency Control d'Oracle,
Firebird et compagnie n'en est d'ailleurs pas très éloigné. La notion
de branche est encore plus explicite dans un produit comme
EBX.Platform qui sait coordonner différents référentiels.

* On gagne aussi d'énormes facilités de réplication, comme avec git.
Si on n'a plus à se soucier de l'intégrité de données "live" parce
qu'elles sont comme noyées dans la glace (ou la carbonite), on peut
alors utiliser des mécanismes de réplication éprouvés tirés du
Peer-to-Peer qui sait transférer de gros volumes de données éclatées
sur plusieurs sites. Un SI d'entreprise avec pour middleware eMule, ça
le fait, non ?

Bien sûr un tel produit qui vise la montée en charge se retrouve face
à des problèmes colossaux qui ont mis vingt ans à être résolus. D'un
autre côté, la seule raison pour avoir des données muables dans un
SGBDR c'est d'économiser l'espace disque, ce qui n'est plus vraiment à
l'ordre du jour et on peut donc s'attendre à des simplifications
draconiennes de ce côté-là. Comme d'habitude en informatique, il ne
s'agit pas d'inventer l'outil qui résoudra tous les problèmes. Il
s'agit juste d'inventer les nouveaux problèmes qui remplaceront ceux
dont on a fini par se lasser (rires).

Tout cela m'amuse beaucoup je paye une pinte à celui qui veut bien
passer deux heures à discuter de la faisabilité d'un tel projet.


Enjoy !

c.

Annexe

Article précédent sur l'immuabilité, le Web et les toolkits graphiques :
http://groups.google.com/group/techos/browse_thread/thread/0873f76a617b7ed4

Article précédent sur git :
http://groups.google.com/group/techos/browse_thread/thread/c88b78c2347330b4

Survol des structures internes de git (très bien fait, plein d'images) :
http://eagain.net/articles/git-for-computer-scientists

Envers, versioning automatique d'entités JPA :
http://www.jboss.org/envers

EBX.Platform d'Orchestra Network,
http://www.orchestranetworks.com/product/features_lifecycle.cfm

Mnesia, la base de donnée distribuée basée sur Erlang :
http://www.infoq.com/news/2007/08/mnesia

La BigTable de Google :
http://labs.google.com/papers/bigtable.html

Henri Tremblay

unread,
Jul 23, 2008, 5:29:13 PM7/23/08
to tec...@googlegroups.com
Hum... pas con... intéressant même... ça règlerait beaucoup de mes
problèmes courants.

Comme d'habitude, quelques commentaires à chaud:

- Je pense que tu sous-estimes un peu la taille qu'aurait une database
fréquemment remise à jour. Entre autres car il faut garder une ligne
pour les suppressions.
- L'audit n'est pas complet car on ne peut pas savoir l'état des
relations (si une table est en relation avec une autre par id, on a
pas la version de la ligne reliée dans la passé). Sauf si chaque
jointure a une version et un id. Mais dans ce cas la mise à jour ne
peut que être moins rapide.

Pour hibernate, encore in paradigm shift. C'est magique mais en fait
faut quand même comprendre sous le capot pour s'y retrouver.

La seule chose que j'ai remarqué c'est que c'est à priori plus facile
lorsque le domaine model est touffu de régler les problèmes hibernates
que les bogues des développeurs sans hibernate.

Ensuite, mes préconisations:

Haute performance: JDBC
Petit domaine model: iBatis ou équivalent
Domain model complexe: Hibernate ou outil d'ORM.


2008/7/22 Laurent Caillette <laurent....@gmail.com>:

Laurent Caillette

unread,
Jul 24, 2008, 10:25:36 AM7/24/08
to tec...@googlegroups.com
Merci Henri pour tes commentaires. Bien sûr quand on a une JVM et un
SGBDR on se sent mieux à l'idée de pouvoir s'appuyer sur Hibernate qui
systématise tout un tas de pratiques absolument pas évidentes. En même
temps ces pratiques sont le fruit de divers compromis à remettre en
question périodiquement.

Pour ce qui est de la taille des données, on arrive à quelque chose
d'énorme, fatalement. Parlons de données "frontales" pour désigner la
toute dernière version de chaque ligne de la base, comme on parle de
l'avancée d'un "front nuageux". Parlons de "profondeur" pour
l'historique derrière ces données frontales. Il est possible de
calculer des "résumés" des versions antérieures et de déporter le
stockage des données "profondes" sur des serveurs d'historique. Comme
le résumé pour une version donnée ne changera jamais on s'affranchit
de divers problèmes de réplication. Dit autrement : le serveur de
données frontales est un cache d'historique. Pour les anciens d'AceTP
: imaginez que le Houskeeping ne nécessiterait //aucun// effort de
développement ! A son niveau, git utilise un mécanisme vaguement
analogue en compressant des agrégats de blobs dès qu'ils sont un peu
anciens. Par contre git n'est pas fait pour déporter une partie de son
historique sur un serveur distant. Pour limiter le volume de données
on peut aussi choisir de jeter toutes les versions en-dessous d'une
certaine profondeur et ne garder que le résumé, comme si on recréait
un référentiel git à partir d'une version de travail.

Pour l'historisation des relations il faut explorer les scénarios.

J'ai l'intuition qu'il faut ajouter une contrainte au niveau du DDL du
type : "si tu as deux données liées A et B, que tu as lu A en version
n et que tu l'écris en version n + m (avec m >= 1) alors il faut que
la version de B n'ait pas évolué (version de B <= n)". C'est
l'équivalent du verrouillage pessimiste de plusieurs tables lors d'une
transaction. Ainsi on n'a pas vraiment d'audit mais une garantie sur
ce qui n'a pas pu arriver.

D'ailleurs regardons comment git gère les suppresions. Il n'essaye pas
de tracer les modifs qui arrivent individuellement à chaque fichier.
Ça paraît évident si l'on considère que l'identité d'un fichier dépend
de son nom qui est suceptible de changer, donc l'identité d'un fichier
n'a pas vraiment de sens (d'où les cabrioles avec CVS et autres). A la
place git conserve des éléments de contenu (blobs) et des descriptions
d'arborescence (trees). D'après Linus il est possible de retrouver le
chemin d'un //bloc de code// déplacé à travers différents fichiers. Ça
ne peut être possible que si on ne pollue pas au passage le checksum
SHA-1 qui identifie la modification. Mais il faut se méfier de ce
genre de comparaison : d'abord avec une base de données la notion
d'identifiant est maîtrisée, d'autre part git est fait pour des
données qui tiennent entièrement sur le filesystem local.

Mais bien sûr il est indispensable d'ajouter dans l'API des fonctions
pour "differ" deux versions. Je pense d'ailleurs que c'est la
définition de l'API qui fournit le bon point d'entrée pour un sujet
aussi vaste. Au moins on s'attaque à un sujet concret : rendre les
SGBDR faciles d'accès aux développeurs.

Vengeaaaance !

c.

Henri Tremblay

unread,
Jul 24, 2008, 4:32:10 PM7/24/08
to tec...@googlegroups.com
Ça se tient. Ça mériterait l'expérience. Il faut se poser aussi des
questions de maintient d'index et de fragmentation de la base mais
dans les grandes lignes ça semble réalisable.

Au sujet des déplacements. Pour une SGBC ça bouge peu. Bon, le
renommage des colonnes et autres mérite d'être gèré toutefois.

Un truc. Clearcase gère aussi les déplacements de fichiers. Comme il
est basé sur un DB, il a un id pour chaque fichier, comme il audite
chaque modification d'un fichier, un fichier déplacé n'a qu'un champ
path qui change mais ça reste le meme fichier avec historique.
D'ailleurs ça faisait qu'il faillait faire attention de faire la
commande clearcase move et non un déplacement manuel sur le disque qui
se transformait ensuite en classique delete -> create.

2008/7/24 Laurent Caillette <laurent....@gmail.com>:

Sylvain Rey

unread,
Jul 25, 2008, 3:54:34 AM7/25/08
to tec...@googlegroups.com
Hello
 
J'ai conscience que c'est relativement restreint part rapport à votre discussion, mais je m'étonne que vous ne parliez pas des Redo Logs d'Oracle. De base, ça résout tout de même 2 des 5 points initiaux de Laurent :
piste d'audit dont l'intégrité est gérée par la base de données
réplication des données (Data Guard s'en sert si je me souviens bien)
 
Et les différents travaux déjà effectués sur le sujet des Redo Logs peuvent vous donner une idée un peu plus précise des tailles nécessaires, histoire de cibler un peu plus le "colossal" avec des données concretes.

Voire, pour rejoindre la dernière réflexion de Laurent : et si tout ce qu'il manquait, c'était des API pour contrôler le Redo Log qui serait une base d'infrastructure pour vos idées ?
 
Sylvain

Laurent Caillette

unread,
Jul 28, 2008, 7:44:23 AM7/28/08
to tec...@googlegroups.com
Merci Sylvain pour la piste du Redo Log.

D'après ce que j'ai compris, il stocke les instructions avant qu'elles
n'aient lieu. Donc pas possible de savoir si une entrée du Redo Log
(comme un insert) a eu vraiment lieu. Et puis en inventant un
sur-système on risque de se laisser polluer par des considérations
propres à celui du "dessous". Ne restent du Redo Log que les infos sur
les volumes. Je n'ai rien trouvé mais pour que ça aie du sens il
faudrait étudier une application en détail.

Il y a peut-être plus d'idées à prendre du côté de Prophet, une base
de données conçue pour fonctionner en mode déconnecté (basée sur la
réplication). L'API applique des principes REST et permet d'accéder à
des versions précédentes. A un moment ils ont même implémenté un
backend Subversion. Il y a quelques idées à creuser telles que le vote
pour la résolution de conflit, ou la réutilisation de résolutions
précédentes. Bon c'est un projet qui a vu le jour il y a trois mois.
L'avantage c'est de pouvoir l'étudier sans avoir à se fader dix ans
d'historique d'un coup.

Henri, tu mentionnais le problème de la fragmentation, mais j'ai
l'impression que ce problème n'est aigü que dans le cas d'une base de
donnée qui stocke des états muables. Pour ce qui est des évolutions de
schéma j'ai l'impression que ça va faire mal, surtout en termes d'API.

Ah oui pour l'API j'imagine de fournir les métadonnées... sous forme
de code Java. Disons que ça serait un sous-ensemble du langage. Allô ?
En tous cas ça permet de jeter d'entrée de jeu toutes ces histoires de
mapping O/R. Je compte bientôt poster une maquette.


Enjoy,

c.


Références

Redo Log :
http://www.ngssoftware.com/research/papers/dissecting-the-redo-logs.pdf
http://www.samag.com/documents/s=9797/sam0507a/0507a.htm


Prophet :
http://syncwith.us/prophet/

Reply all
Reply to author
Forward
0 new messages