Sommaire
Bonjour à tous,
Quelques nouvelles de \BlueLaTeX
Je profite de l'occasion d'avoir fait hier la première Release Candidate de \BlueLaTeX pour reparler de ce projet.
Pour rappel, \BlueLaTeX est une plateforme d'édition collaborative de documents écrits en \LaTeX qui consiste en un serveur exposant une API Restful pour gérer les documents, leur synchronisation et leur compilations, et d'une application web jouant le rôle de client (bien que ce soit le seul client actuellement, n'importe quel éditeur de texte peut potentiellement être étendu pour devenir un client \BlueLaTeX), le tout sous licence Apache version 2.0.
Cette version est bien stabilisée par rapport à la version Bêta dont j'avais parlé précédemment, et les différents retours qui avaient été faits ici ou sur le canal IRC ont été pris en compte.
Parmi les améliorations notables, nous retrouvons:
- la compilation est (dans la configuration par défaut) lancée explicitement par les auteurs, plus de processus en arrière plan qui semblait ne pas être réellement intuitif,
- plusieurs compilateurs \LaTeX son supportés par défaut:
- latex + dvi2pdf,
- pdflatex,
- xelatex,
- lualatex ;
- des tooltips ont été ajoutés sur tous les boutons et icônes,
- la complétion dans l'éditeur est devenue plus performante,
- beaucoup d'autres de petites et grosses améliorations et corrections ont été faites, notamment pour rendre le client plus rapide, stable et intuitif,
- une vraie distribution installable est maintenant à télécharger, avec des scripts permettant de lancer le serveur en tant que démon via jsvc, ou (au choix) une unité systemd,
- une instance de démonstration est maintenant disponible, avec un compte test/test (s'il n'a pas été supprimé et qu'aucun petit malin n'a modifié le mot de passe), ou bien vous pouvez vous créer un compte.
L'écriture d'un manuel utilisateur pour le client web est en cours et devrait sortir pour la version 1.0.0.
Si aucun problème majeur n'a été trouvé sur cette version elle pourra devenir la première version stable de \BlueLaTeX !
Pour le côté utilisateur voilà le résumé, vous pouvez aller faire un tour du côté des tickets fermés pour plus de détails.
Côté technique
La gros changement technique qui s'est produit depuis la première Bêta vient du modèle de données qui était trop statique par rapport au but que nous souhaitons atteindre.
Après avoir lu la série de dépêches de rewind sur la création de jeux vidéo, et notamment la première de la série, j'ai opté pour un modèle de données implémenté en système à entités.
Je ne vais pas refaire de tutoriel sur ce qu'est un tel système, relisez la dépêche de rewind et les liens associés pour vous rafraichir la mémoire si besoin.
La problématique
\BlueLaTeX est architecturé de manière à pouvoir facilement être étendu par de nouveaux composants côté serveur.
Par exemple la version de base est architecturée ainsi:
- le composant
core
qui fournit les services de base nécessaire au bon fonctionnement de l'application, comme la gestion des utilisateurs et des documents, - le composant
sync
qui fournit le service de synchronisation des sources entre les différents utilisateurs, - le composant
compiler
qui fournit les services liés à la compilation des document \LaTeX.
Chacun de ses composants peut avoir besoin de faire persister des données nécessaires à son fonctionnement.
Concrètement dans les trois composants de base suscités, seuls core
et compiler
ont besoin de le faire pour le moment.
Le composant core
enregistre les informations sur les utilisateurs, les papiers et les rôles des utilisateurs sur les papiers, quant au composant compiler
il enregistre la configuration du compilateur associé au document.
Ces composants sont très indépendants au niveau des données et aucun lien ne les unie, si ce n'est le papier en lui même. De plus, certains composants peuvent ne pas être présents (le compilateur est tout à fait optionnel si le client décide de faire la compilation du \LaTeX de son côté).
Il nous fallait donc un modèle de données flexible qui permet facilement l'ajout et le retrait de données indépendantes, sans compromettre l'intégrité des données existantes.
Nous utilisons CouchDB comme serveur de base de données, ce qui nous permet de stocker nos données sans schéma précis.
C'est bien, l'outil nous permet de faire ce que nous voulons, mais il faut le faire à la main, gérer les documents correctement et les suppressions des documents attachés à un objet qui n'existe plus.
De plus le fait de devoir le faire à la main avait conduit à avoir un modèle de données très monolithique au final avec des documents fourre-tout.
Bref, une solution était nécessaire !
Entités dans CouchDB
Le principe des entités et composants m'a plu dès le premier article que j'ai lu sur le sujet, et le côté dynamique de la forme des données m'a paru assez adapté à la gestion de documents CouchDB.
Il est temps pour moi de vous présenter la bibliothèque que je développe en marge de \BlueLaTeX : sohva.
Originellement issue du code de l'ancienne version de \BlueLaTeX, cette bibliothèque est devenue un ensemble d'outils permettant d'accéder et de gérer des données stocker dans CouchDB en scala.
La version en cours de développement contient un client et s'est vue ajouter plusieurs composants dont un en particulier qui nous intéresse ici : sohva-entities
.
Ce composant est construit au dessus du client par défaut et permet donc de gérer des systèmes à entités en utilisant CouchDB système de persistance des données.
La manière d'accéder à ces fonctionnalités est l'EntityManager
, que l'on obtient à partir d'une référence à une base de données.
Le but ici n'est pas de faire un tutoriel de la bibliothèque, de scala ou de CouchDB, mais globalement voilà comment un tel manager est créé :
import akka.actor.ActorSystem
import akka.util.Timeout
import scala.concurrent.duration._
import gnieh.sohva.sync.entities._
implicit val system = ActorSystem("entities-system")
implicit val timeout = Timeout(20.seconds)
val couch = new CouchClient
val database = couch.database("entitiy-database")
implicit val manager = new EntityManager(database)
Ainsi donc, un gestionnaire d'entités est attaché à une base de données en particulier, dans laquelle il stocke les entités et les composants.
L'API du gestionnaire est assez classique d'après ce que j'ai pu voir dans la littérature sur le sujet (signatures simplifiées ici pour des raisons de lisibilité):
type Entity = String
trait EntityManager {
def createSimple(): Entity
def createTagged(tag: String): Entity
def create(uuid: String, tag: Option[String]): Unit
def deleteEntity(entity: Entity): Boolean
def saveComponent[T <: IdRev](entity: Entity, component: T): T
def hasComponentType[T](entity: Entity): Boolean
def hasComponent[T](entity: Entity, component: T): Boolean
def getComponent[T](entity: Entity): Option[T]
def removeComponentType[T](entity: Entity): Boolean
def removeComponent[T <: IdRev](entity: Entity, component: T): Boolean
def entities: Set[Entity]
def entities(tag: String): Set[Entity]
}
Comme dans beaucoup de ces systèmes, une entité n'est qu'un identifiant (ici une chaîne de caractères) auquel nous pouvons attacher des composants contenant des données à associer.
Concrètement en base de données, un entité est représentée par un document contenant uniquement l'identifiant et éventuellement un champ tag
, permettant d'étiqueter les entités.
Pour que la mécanique du gestionnaire marche, le document a aussi d'autres champs, permettant de gérer tout ça correctement.
Lorsqu'un composant est attaché à une entité, celle-ci n'est pas modifiée, et un nouveau document correspondant à ce composant est enregistré dans la base de données.
Ceci explique la présence du IdRev
qui est un trait de sohva indiquant que nous avons à faire à un objet avec un champ obligatoire _id
et un champ optionnel _rev
, pré-requis des documents CouchDB.
De manière similaire aux entités, d'autres champs sont ajoutés par le gestionnaire afin d'identifier ces documents comme composants et de les rattacher à leur entité.
Et voilà, c'est tout : un document pour l'entité, un par composant attaché à une entité.
Le lien entre une entité et ses composants est fait par une vue.
L'avantage de cette approche dans CouchDB et de ne pas avoir fait un seul document par entité avec un tableau de composants par exemple, et que chaque composant peut être géré et sauvegardé séparément, éliminant de potentiels conflits si deux composants d'une même entités sont enregistrés en parallèle.
Bien sûr cette manière de procéder fait que plus de documents sont créés en base de donnée, mais le gain est grand si l'on évite les conflits, et permet d'éviter de nouveaux problèmes si l'on a mis en place de la réplication entre plusieurs instances.
Pour la suppression, tout est géré automatiquement et tous les documents de composants attachés à un document d'entité sont supprimés lorsque celle-ci est elle-même supprimée.
Une limitation actuelle de la bibliothèque est qu'un seul composant de chaque type peut être attaché à une entité, c'est sûrement une limite artificielle et qui serait simple à retirer, mais je n'en ai pas encore vu d'utilité.
Dans les systèmes existants j'ai vu les deux cas sans vraiment avoir d'avis sur la question. Je m'en remets à vous pour argumenter pour ou contre cette limite.
Voilà à quoi ressemble la création et la manipulation d'une entité et de ses composants avec l'exemple d'un vaisseau spatial dans un jeu avec ses composants (exemple issu de cet article) :
case class PositionComponent(_id: String, x: Double, y: Double) extends IdRev
object PositionComponent {
def apply(x: Double, y: Double): PositionComponent =
new PositionComponent(generateId(), x, y)
}
case class RotationComponent(_id: String, angle: Double) extends IdRev
object RotationComponent(angle: Double): RotationComponent =
new RotationComponent(generateId(), angle)
}
val spaceship = manager.createTagged("spaceship")
manager.saveComponent(spaceship, RotationComponent(180))
// ou avec un peu de 'type classes' et d'implicites
spaceship.save(PositionComponent(200, 400))
Un système ensuite accède aux entités et leurs composants comme suit:
for {
entity <- entities("spaceship")
position <- manager.getComponent[PositionComponent](entity)
// ou avec un peu de 'type clases' et d'implicites
rotation <- entity.get[RotationComponent]
} {
// faire le travail nécessaire
}
La partie "systèmes" n'est pas incluse dans sohva-entities
qui s'occupe de la gestion et de la persistance des données uniquement et pas de la logique.
Chaque utilisateur et donc libre d'implémenter les systèmes à sa manière : traitement périodique, gestionnaire d'événements, autre…
Ce que \BlueLaTeX y gagne
La première mise en œuvre de cette bibliothèque est donc dans \BlueLaTeX. Le modèle de données a été adapté pour transformer documents et utilisateurs en entités.
Les différentes données attachées ont été transformées en composants. Ainsi lors de la création d'un nouveau papier le système noyau crée les entités et composants suivants :
- entité papier,
- composant
core
qui contient le nom du document, sa date de création, son type (pour le moment c'est du \LaTeX uniquement, mais markdown va suivre), - composant
roles
qui contient la liste des auteurs et relecteurs.
Pour sa part, le module compiler
ajoute son propre composant compiler
(Étonnant, non ?) qui contient le nom du compilateur à utiliser (pdflatex, xelatex, lualatex, …), le timeout de compilation, …
La suppression des utilisateurs et documents s'est vue grandement simplifiée par le nettoyage automatiquement géré lors de la suppression d'une entité, les données sont clairement séparées selon leur utilité et chaque système ne lit et ne met à jour que les documents qu'il utilise, ce qui permet d'éviter des conflits si deux systèmes sauvegardent des données orthogonales en même temps.
Pour le futur, il est envisageable (et envisagé d'ailleurs) d'avoir un composant en plus pour les papiers comme la phase dans laquelle le papier se trouve, et le permissions associées à cette phase, ou encore des informations de tags et branches associées au cycle de vie du document, ou, ou, ou…
Un gros avantage au fait que chaque nouveau module côté serveur puisse ajouter ses données sous forme de composant et que cela n'impacte personne autour de lui (à condition bien sûr de ne pas jouer avec les composants des autres sans leur demander gentiment leur avis avant).
Bien sûr ce changement casse la compatibilité avec le modèle de données de la première Bêta, mais j'ai estimé que c'était le moment de le faire avant que ce soit utilisé en production et que la migration soit pénible.
Et la suite ?
Maintenant que les fonctionnalités souhaitées pour la première version sont réalisées, que le futur est plutôt bien préparé pour les évolutions prévues à court et moyen terme, que le client et le serveur ont été stabilisés, le gros du travail est axé sur la documentation pour la première version stable, et sur les premières évolutions présentées sur le site.
# Merci
Posté par barmic . Évalué à 5.
Journal très intéressant (qui va finir en dépêche je présume).
On voit un exemple de système à entité ailleurs que dans un jeu et ça en soit c'est super cool.
J'ai quelques questions :
Encore merci d'avoir pris le temps de faire ce journal.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Merci
Posté par Lucas . Évalué à 4.
Merci.
Pour répondre à tes deux points :
Je me suis demandé la même chose et la réponse de la documentation dit que lorsque l'on arrive dans les millions ça commence à poser des problèmes. Pour ces cas extrêmes j'ai réfléchi au fait de pouvoir choisir la manière dont sont stockés les composants, et de les embarquer dans un seul document dans un champs, disons,
components
. Je pense qu'en fait le cas d'usage influe beaucoup sur ce qui fait sens. Dans un premier temps pour \BlueLaTeX j'ai privilégié l'approche qui réduit les conflits. À vrai dire, les documents sont liés entre eux de manière assez simple et les vues nécessaires pour les gérer le sont aussi, notamment, il n'y a pas de fonction reduce, mais uniquement un index des composants par entité et par type. Le protocole entre CouchDB et le server de query est particulièrement mauvais pour les fonctions reduce en l'état, un meilleur protocole est en cours de développement et devrait améliorer les latences. Je n'ai pas remarqué de lenteurs particulières, et des utilisateurs de sohva m'ont fait part de bonnes performances même avec des bases assez chargées. Si les vues peuvent être construites de manière incrémentielle, et que l'on ajoute les données au fil de l'eau, alors le temps passé à construire les vues est aussi mieux réparti et ne se fait pas trop sentir. Le cas des entités dans sohva doit rentrer dedans. J'avoue que je n'ai pas fait de tests de limites pour le moment, c'est toujours en développement mais je vais le planifier :)En fait les composants représentent les méta données d'un projet LaTeX du point de vue \BlueLaTeX. Les fichiers TeX quant à eux sont enregistrés normalement sur le disque dur et compilé sur le disque de manière tout à fait normale.
En base de données ne se trouvent que les données nécessaires à la gestion des permissions, des cycles de vie, des utilisateurs, etc… Typiquement le composant
compiler
d'un papier a cette tête là :Et sert en quelque sorte de descripteur de build pour le papier avec l'id
x1ef7eb26809749fb
:Si on regarde le composant
core
de ce même papier, on verra :Qui contient la date de création et le nom donné par l'utlisateur à ce papier.
Bref, ce sont uniquement des meta données sur le papier, spécifiques à \BlueLaTeX, il n'y a aucune surcouche à LaTeX écrite pour ce qui est de la compilation des documents.
J'espère avoir clarifié les choses sinon hésite pas à demander :)
# scala & akka
Posté par Benoît Laurent (site web personnel) . Évalué à 6.
À l'occasion si tu veux faire un journal ou une dépêche sur l'utilisation de scala & akka et dans le style de ce celui-ci ça serait très agréable et intéressant.
[^] # Re: scala & akka
Posté par Lucas . Évalué à 1.
Ce serait en effet une bonne idée, on en fait une grosse utilisation dans tous les composants de \BlueLaTeX. Il faut que je trouve le temps de faire un truc cohérent, sinon yapuka :)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.