Skip to content

Blogs de Développeurs: Aggrégateur de Blogs d'Informatique sur .NET, Java, PHP, Ruby, Agile, Gestion de Projet

Forum Logiciel

Forum Logiciel : diffusion de connaissance et d’informations sur toutes les activités liées au développement d’applications informatiques en entreprise.

Blog Société

Petit-déjeuner : Un éléphant qui se balançait … – jeudi 5 octobre 2017

Comment mettre en musique les big data et valoriser les données avec de nouveaux services.

Petit déjeuner - un éléphant qui se balançait

BNP Paribas viendra témoigner de sa démarche avec un retour sur la mise en œuvre de ces nouvelles architectures de données.

Un menu copieux pour cette rentrée des petits-déjeuner OCTO avec un focus sur les architectures de données, un témoignage de BNP Paribas, un retour sur la mise en œuvre de ces nouvelles architectures de données et, cerise sur le gâteau, une mise en perspective de la tendance vers des architectures de flux à l’occasion de la publication du livre blanc Digital Studies Vol.02 : La question du temps dans les architectures digitales.

Les données sont là, initialement éclatées dans différents silos applicatifs. Mais maintenant qu’elles commencent à alimenter un Data Lake sous Hadoop, que va-t-on en faire ? Comment les valoriser ? Comment créer de nouveaux services à valeur ajoutée ?

BNP Paribas viendra tĂ©moigner de sa dĂ©marche – initiĂ©e par des expĂ©rimentations autour des data – pour proposer dès Ă  prĂ©sent de nouveaux services (trois projets seront Ă©voquĂ©s).

OCTO vous prĂ©sentera le retour d’expĂ©rience sur la mise en Ĺ“uvre de ces nouvelles architectures de donnĂ©es, incluant les technologies Hadoop, Spark, Cassandra, Solr ainsi que des expĂ©rimentations sur le Machine Learning, tout en soulignant les mĂ©thodes de travail utilisĂ©es avec des Ă©quipes mixtes BNP Paribas / OCTO.

Ce petit-déjeuner sera aussi l’occasion de vous présenter et de vous remettre une version imprimée du livre blanc Digital Studies Vol.02, consacré aux questions d’architecture, notamment aux nouvelles architectures de flux.

 

Cliquez ici pour vous inscrire au petit-déjeuner « Un éléphant qui se balançait »

 

Articles suggested :

  1. Formation Data Science : Paris – Genève
  2. USI dans VOTRE entreprise avec MyUSI
  3. Formations OCTO : Novembre – DĂ©cembre

Catégories: Blog Société

Les KProperty2 ou la réflexion signée Kotlin

En parcourant la bibliothèque standard de Kotlin, section réflexion, on peut tomber sur des types tels que KProperty0, KProperty1 et KProperty2.

On comprend assez rapidement que les types KProperty* sont des types qui reprĂ©sentent et permettent de manipuler, par rĂ©flexion, des propriĂ©tĂ©s i.e. des variables dĂ©clarĂ©es dans un package ou dans une classe. On comprend, en revanche, un peu plus difficilement pourquoi des propriĂ©tĂ©s peuvent ĂŞtre reprĂ©sentĂ©es par plusieurs types (et surtout par trois types diffĂ©rents). En Java, par exemple, l’Ă©quivalent d’une propriĂ©tĂ© – un champ – est toujours reprĂ©sentĂ© par le type Field, qu’il soit statique ou non.

Cet article vous propose de dĂ©couvrir ce que les diffĂ©rents types KProperty* reprĂ©sentent. Nous commencerons donc par Ă©tudier les types KProperty0 et KProperty1 qui se rĂ©vèlent, au final, assez simples. Nous pourrons ensuite mieux aborder le type KProperty2, la configuration un peu inhabituelle qu’il reprĂ©sente et surtout en quoi il peut bien nous ĂŞtre utile.

Le type KProperty0

Comme le montre le code ci-dessous (lien gist), le type KProperty0 reprĂ©sente une variable immuable dĂ©clarĂ©e au niveau d’un package (sans aucun contexte).

package com.github.sergiords.kproperties

import kotlin.reflect.KProperty0

val name: String = "Bob"

fun main(args: Array<String>) {

    val kProperty0: KProperty0<String> = ::name

    val value: String = kProperty0() // value of the property "name"

    println(value) // prints "Bob"

}

Dans cet exemple, pour obtenir une instance du type KProperty0 reprĂ©sentant la variable immuable name on utilise l’opĂ©rateur « :: » (ligne 9) appliquĂ© Ă  la variable elle-mĂŞme. On voit que le type KProperty0 est paramĂ©trĂ© par le type de la propriĂ©té qu’il reprĂ©sente (String). L’instance kProperty0 peut ensuite ĂŞtre utilisĂ©e pour rĂ©cupĂ©rer la valeur de la variable (ligne 11).

Le type KProperty1

Le code ci-dessous (lien gist) met quant Ă  lui en Ă©vidence le type KProperty1 qui reprĂ©sente une variable immuable dĂ©clarĂ©e au niveau d’une classe (dans le contexte d’un type User).

package com.github.sergiords.kproperties

import kotlin.reflect.KProperty1

class User {

    val name: String = "Bob"

}

fun main(args: Array<String>) {

    val kProperty1: KProperty1<User, String> = User::name

    val user: User = User()

    val value: String = kProperty1(user) // value of the property "name" inside "user" instance

    println(value) // prints "Bob"

}

Pour obtenir une instance du type KProperty1 reprĂ©sentant la variable immuable name dans une instance du type User on utilise l’opĂ©rateur « :: » appliquĂ© au type User (ligne 13). On voit que le type KProperty1 est paramĂ©trĂ© par le type contenant la variable (User) et par le type de la propriĂ©té qu’il reprĂ©sente (String). L’instance kProperty1 peut ensuite ĂŞtre utilisĂ©e pour rĂ©cupĂ©rer la valeur de la variable immuable dans une instance du type User (ligne 17).

Le type KProperty2

Nous venons de voir les types KProperty0 et KProperty1, ils reprĂ©sentent respectivement une variable immuable dĂ©clarĂ©e au niveau d’un package (zĂ©ro contexte) et au niveau d’un type donnĂ© (un seul contexte, l’instance). Pour le type KProperty2 on suit le mĂŞme raisonnement en ajoutant un contexte. Le type KProperty2 reprĂ©sente donc une variable immuable dĂ©clarĂ©e dans deux contextes. Comment est-ce possible ? Avec les extensions de propriĂ©tĂ©s, mises en Ă©vidence ci-dessous (lien gist).

package com.github.sergiords.kproperties

import kotlin.reflect.KProperty2
import kotlin.reflect.full.declaredMemberExtensionProperties

class Magic {

    val String.magicLength: Int   // add property magicLength to String type inside Magic class only
        get() = this.length * 42

    fun length(arg: String): Int = arg.magicLength

}

fun main(args: Array<String>) {

    @Suppress("UNCHECKED_CAST")
    val kProperty2: KProperty2<Magic, String, Int> = Magic::class.declaredMemberExtensionProperties.single() as KProperty2<Magic, String, Int>

    val magic = Magic()

    val abcdValue: Int = kProperty2(magic, "ABCD") // value of the property "magicLength" inside "ABCD" and "magic" instances
    println(abcdValue) // since "ABCD".length = 4, prints 4 * 42 = 168

    val abcdeValue: Int = magic.length("ABCDE") // value of the property "magicLength" inside "ABCDE" and "magic" instances
    println(abcdeValue) // since "ABCDE".length = 5, prints 5 * 42 = 210

}

La syntaxe un peu particulière des lignes 8 et 9 permet de rattacher une variable immuable magicLength, au type String, uniquement accessible dans une instance du type Magic. Le this de la ligne 9 reprĂ©sente bien l’instance du type String qui reçoit cette nouvelle variable immuable. Cette syntaxe est une extension de propriĂ©tĂ© un peu particulière car elle est dĂ©clarĂ©e dans le contexte d’un autre type et c’est cette configuration de variable immuable que reprĂ©sente le type KProperty2.

Pour obtenir une instance du type KProperty2, on doit cette fois s’appuyer sur les extensions de propriĂ©tĂ©s dĂ©clarĂ©es au niveau du type Magic (ligne 18). On voit bien que le type KProperty2 est paramĂ©trĂ© par le type dans lequel il est dĂ©clarĂ© (Magic), par le type qui reçoit l’extension de propriĂ©tĂ© (String) et par le type de la propriĂ©tĂ© qu’il reprĂ©sente (Int). L’instance de kProperty2 peut ensuite ĂŞtre utilisĂ©e pour obtenir la valeur de la variable immuable magicLength d’une chaĂ®ne « ABCD » dans une instance donnĂ©e du type Magic (ligne 22).

Une utilisation plus standard de ces propriétés est la fonction Magic::length qui utilise cette extension de propriété, mais cette fois, directement dans le type Magic (déclaration à la ligne 11 et appel à la ligne 25).

Conclusion

L’objectif de cet article Ă©tait avant tout de comprendre ce que les diffĂ©rents types KProperty* pouvaient bien reprĂ©senter comme configuration. Si les types KProperty0 et KProperty1 reprĂ©sentent une configuration assez classique, il n’en est rien pour le type KProperty2.

La question qui nous vient alors naturellement Ă  l’esprit est la suivante : « mais Ă  quoi cela peut-il bien servir ? ». On peut voir le type KProperty2 comme la possibilitĂ© de reprĂ©senter des propriĂ©tĂ©s qui ne sont valables que lorsque deux contextes sont rĂ©unis. On peut, par exemple, reprĂ©senter la position d’une forme dans un conteneur avec le code ci-dessous (lien gist).

package com.github.sergiords.kproperties

class Form(val x: Int, val y: Int)

class Container(val x: Int, val y: Int) {

    val Form.xInContainer: Int
        get() = this.x - this@Container.x

    val Form.yInContainer: Int
        get() = this.y - this@Container.y

}

Dans l’exemple ci-dessus, une forme peut ainsi avoir une position absolue (Form.x, Form.y) et une position relative dĂ©finie dans un conteneur (Form.xInContainer, Form.yInContainer). Cette dernière position bien que rattachĂ©e au type Form, n’est dĂ©finie que dans le contexte du type Container. En dehors de ce contexte elle n’a pas de sens et n’est d’ailleurs pas accessible.

Catégories: Blog Société

How the right DevOps tools can help bring teams together

Le blog de Valiantys - mar, 09/19/2017 - 14:23

Adopting DevOps is an accumulation of a number of small steps towards a common direction. In this short article, I like to cover one of those small steps in making DevOps work for you – getting your teams to use the same platform. DevOps started with software teams switching successfully to Agile methodologies. Development cycles ...

The post How the right DevOps tools can help bring teams together appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Pour une vague d’adrénaline

ekito people - mar, 09/19/2017 - 11:03

Le secret ? Tout ce qu’il y a autour.

Si tu commences l’aventure sans apprécier souffrir, ramer, attendre, avoir froid, douter, cela va être laborieux.

Parce que dans ce domaine le ratio résultat sur effort n’est pas bon. Bien pire qu’ailleurs.

 

Photo by Roger Mosley.

Beaucoup de travail.

Des journées à avoir l’impression que rien ne fonctionne. Que malgré tous tes efforts, tu n’es jamais au bon endroit au bon moment. Pour un détail mal ajusté, tu réapprends l’humilité. Violemment. L’impression de manquer d’air.

Le contrĂ´le.

Et sa possibilité de le perdre. Ce rappel permanent que face aux éléments tu es vulnérable. Cette absence de certitude qui fait que chaque vague est magique. Ce moment où le temps s’arrête et se compresse. Cette descente infinie, ce sentiment de puissance gravé à jamais dans ton esprit par la vitesse. Cette éphémère omnipotence.

C’est pour tout ça que tu y retournes malgré tout. Pour cette adrénaline.

Pour ces 5 secondes d’éternité.

The post Pour une vague d’adrénaline appeared first on ekito people.

Catégories: Blog Société

Tester du code Spark – 2 – La pratique

Que de la thĂ©orie. Les tests du code Spark semblent abonnĂ©s Ă  cette rĂ©alitĂ© : tout reste thĂ©orique. Ă€ croire qu’en rĂ©aliser est impossible. Il est grand temps de remĂ©dier Ă  ce problème et dĂ©montrer que le sujet est abordable par tous.

Quitter la thĂ©orie, c’est dĂ©buter par les bonnes pratiques. Des rĂ©flexes sur la spĂ©cification des jeux de tests jusqu’aux extensions des DSL Scala et Python qui simplifient les assertions autour des DataFrames en passant par l’utilisation de Spark-Testing-Base, cet article couvre le minimum nĂ©cessaire Ă  l’Ă©criture des tests en Spark.

AmĂ©liorer la lisibilitĂ© du contexte d’exĂ©cution du test

Dans un cas idĂ©al, un test contient trois sections : « Given », « When », et « Then ». Le « Given » dĂ©fini clairement le contexte d’exĂ©cution du test qui le rend spĂ©cifique. C’est ce contexte, appliquĂ© Ă  notre mĂ©thode testĂ©e (le « When ») qui provoque la sortie attendue (le « Then »). Il est important de chercher Ă  rĂ©duire la quantitĂ© d’informations prĂ©sentes dans ce contexte d’exĂ©cution dans l’objectif d’amĂ©liorer sa lisibilitĂ©.

Premièrement, oĂą doivent se situer les donnĂ©es de test ? Doivent-elles ĂŞtre externalisĂ©es dans un fichier dĂ©diĂ© ? Ou bien doivent-elles ĂŞtre insĂ©rĂ©es directement dans le code du test ? Mon prĂ©cĂ©dent article prĂ©conise de les intĂ©grer dans le code du test, Ă  l’exception des tests de validation d’un modèle de Machine Learning. Ă€ première vue, la solution du fichier permet de fournir un jeu de test plus consĂ©quent. Cependant, augmenter la taille du jeu de test en entrĂ©e entraĂ®ne de la difficultĂ© en termes de maintien de la cohĂ©sion entre le test et ses donnĂ©es.

Deuxièmement, comment initialiser un DataFrame contenant potentiellement beaucoup de colonnes ? La solution est en fait plutôt simple.

En Scala, la mĂ©canique de « case class » associĂ©e Ă  la mĂ©thode implicite d’une session Spark permet de conserver un maximum de simplicitĂ© comme le montre l’exemple suivant.

import org.apache.spark.sql.DataFrame
import org.scalatest.{FlatSpec, GivenWhenThen}

case class TestRow(column1: String = "", column2: String = "")

class SimpleSpec extends FlatSpec with GivenWhenThen with SparkSessionProvider {
  import sparkSession.implicit._
  
  "A Unit test" should "have a readable Given" in {
    Given("A specific value on column 1")
    val inputDataFrame: DataFrame = List(
      TestRow(column1 = "specific value")
    ).toDF()
    
    ???
  }
  
}

En Python, le « kwargs » fourni quelque chose de semblable Ă  la version Scala. Bien que plus lĂ©gère Ă  première vue, cette version convient principalement Ă  des cas simples de valeurs prĂ©cises. Toute inclusion d’une valeur nulle (None) impose d’initialiser et de fournir un schĂ©ma SparkSql (une instance de StructType).

import unittest2

spark_context = SparkContext()
spark_session = SparkSession(spark_context).builder.master("local[*]").appName("Unit tests").getOrCreate()
 
class TestSimple(unittest2.TestCase):
 
  def test_shoud_have_readable_give(self):
    # Given
    input_data_frame = spark_session.createDataFrame([
      Row(column1 = "specific value")
    ])
    ....

La comprĂ©hension du test est simple, en Python comme en Scala : le DataFrame d’entrĂ©e contient une ligne dont la particularitĂ© porte sur la colonne « column1 ».

Spark-Testing-Base, de nombreuses fonctionnalités pour différents langages

Une des bibliothèques les plus connues autour du test de programmes Spark est sans conteste Spark-Testing-Base. Cette bibliothèque propose de nombreuses fonctionnalitĂ©s pour diffĂ©rents langages (Scala, Java, Python) parmi lesquelles des classes d’aide Ă  la rĂ©daction d’assertions sur les RDD, DataFrames et DataSets et de tests basĂ©s sur l’utilisation d’un Mini-Cluster. Elle propose Ă©galement du support autour de ScalaCheck.

Une configuration simple

En Scala comme en Python, la procédure de configuration est simple.

En Scala, elle est dĂ©crite directement dans le readme du dĂ©pĂ´t Github du projet. De base, un ajout de dĂ©pendance Maven ou SBT et le tour est jouĂ© (il est toutefois recommandĂ© de correctement dimensionner la mĂ©moire car l’utilisation d’un contexte Spark local est assez gourmand). Attention Ă  bien prĂ©ciser la version voulue de Spark dans la dĂ©pendance.

val sparkVersion = "2.2.0"
val sparkTestingBaseVersion = "0.7.4"
libraryDependencies += "com.holdenkarau" %% "spark-testing-base" % s"${sparkVersion}_$sparkTestingBaseVersion" % "test"
    <dependency>
        <groupId>com.holdenkarau</groupId>
        <artifactId>spark-testing-base_${scala.version}</artifactId>
        <version>${spark.version}_${sparktestingbase.version}</version>
        <scope>test</scope>
    </dependency>

En Python, le package est disponible sur le dépôt PyPi comme tout autre package. Il est donc installable via PIP par la commande pip install spark-testing-base. Il suffit ensuite de placer la dépendance dans le fichier setup.py dans la zone tests_requires :

from setuptools import setup
 
setup(
  ...
  tests_requires=['spark-testing-base']
  ...
)

Cette bibliothèque propose trop de contenu pour qu’il soit totalement couvert dans cet article. Probablement sa fonctionnalitĂ© la plus utile, la classe DataFrameSuiteBase fourni le nĂ©cessaire Ă  un test simple dans Spark, en particulier des fonctions utilitaires pour la comparaison de DataFrames. Voici comment les utiliser :

class SimpleSpec extends FlatSpec with DataFrameSuiteBase {
  "Simple Spec" should "show how to use DataFrameSuiteBase" in {
    // Given
    val df = List(
      Balance(income=3, outcome=2)
    ).toDF()
 
    // When
    val result = df.filter(positiveAmount(_))
 
    // Then
    assertDataFrameEquals(df, result)
  }
 
  it should "also show to to deal with errors" in {
    // Given
    val df = List(
      Balance(income=2, outcome=3)
    ).toDF()


    // When
    val result = df.filter(positiveAmount(_))


    // Then
    intercept[TestFailedException]
      assertDataFrameEquals(df, result)
  }
}

Une fonctionnalité semblable est disponible dans la version Python de la bibliothèque. Il faut utiliser la classe SQLTestCase. Voici comment faire :

from pyspark.sql import Row
from sparktestingbase.sqltestcase import SQLTestCase
import unittest2

class SimpleTest(SQLTestCase):
  def test_do_nothing(self):
    rdd = self.sc.parallelize([Row(
        column1="foo"
    )])
    df = rdd.toDF()
    
    self.assertDataFrameEqual(df, df)
À utiliser pour des besoins spécifiques

Il est indĂ©niable qu’un framework tel que Spark-Testing-Base rend le code plus lisible. La quantitĂ© de mĂ©thode type « helpers » apporte une facilitĂ© d’Ă©criture des tests. Sur un petit projet, la simplicitĂ© de mise en Ĺ“uvre est flagrante.

Son utilisation apporte nĂ©anmoins quelques complexitĂ©s un peu cachĂ©es au premier abord. L’utilisation de cette bibliothèque par Maven/SBT provoque souvent le tĂ©lĂ©chargement d’une quantitĂ© astronomique de dĂ©pendances afin d’avoir Spark-Testing-Base Ă  disposition. Il arrive d’ailleurs parfois d’avoir des conflits de dĂ©pendances apportĂ©s par les dĂ©pendances transitives de Spark-Testing-Base, mais rien d’insoluble. Dans de nombreux cas, le besoin se situe au niveau d’une ou deux classes assez simples de cette bibliothèque. Beaucoup d’Ă©lĂ©ments seront Ă  ramener pour finalement quelques lignes de code. PrĂ©conisation personnelles : rĂ©Ă©crire le petit morceau de code Ă©quivalent de cette bibliothèque pour rĂ©pondre Ă  son besoin.

Enfin, un dernier point nĂ©gatif de ce framework est qu’il est moins fourni en Python, de quoi satisfaire principalement les utilisateurs de Spark en Scala.

En définitive, ce framework est à utiliser pour des besoins spécifiques. Certaines classes utilitaires sont assez complexes (telles que celles relatives à ScalaCheck) et il ne serait pas judicieux de les réécrire.

Simplifier l’Ă©criture des tests Spark en Scala

Lors de l’Ă©criture des tests Spark en Scala, il est classique d’utiliser les matchers de ScalaTest pour valider les attendus. Ceci fonctionne très bien pour valider des Ă©lĂ©ments tels que le contenu des schĂ©mas (les champs sont une collection de StructTypes que l’on peut valider) ou le contenu des DataFrames. Il s’agit d’une collection de Row. Puisque dans les tests, la quantitĂ© de donnĂ©e est limitĂ©e, la mĂ©thode collect est utilisable. Il est alors possible de tout ramener sur le driver pour utiliser les collections de base de Scala, donc les matchers de base, sans exploser la consommation mĂ©moire.

Voici un exemple type de test de schéma et de contenu de DataFrame :

class SimpleSparkAndScalaTest extends FlatSpec with Matchers with SparkSessionProvider {
  import sparkSession.implicit._

  def onlyPositiveBalance(inputDataFrame: DataFrame): DataFrame = {
    def positiveBalance(input: Row): Boolean = {
      input.getInt(input.fieldIndex("income")) - input.getInt(input.fieldIndex("outcome")) > 0
    }
    inputDataFrame.filter(positiveBalance(_))
  }
  
  case class Balance(id: Int, income: Int = 0, outcome: Int = 0)
  
  "Simple test" should "do something" in {
    // Given
    val inputDF = List(
      Balance(id = 1, income = 3, outcome = 2),
      Balance(id = 2, income = 3, outcome = 4)
    ).toDF()
    
    // When
    val result = onlyPositiveBalance(inputDF)
    
    // Then
    result.schema.map(_.name) should contain allOf ("income", "outcome")
    result.count() shouldBe 1
    result.map(row => row.getInt(row.fieldIndex("id"))).head shouldBe 1
  }

}

La couverture de test est Ă  un bon niveau puisque tout le code peut ainsi ĂŞtre testĂ©. Mais Ă  la lecture, il y a quelque chose de très dĂ©rangeant. La dernière ligne est trop complexe et est donc difficile Ă  lire. Il est grand temps de simplifier l’Ă©criture des tests en Scala.

Pour répondre à ce besoin, quelques lignes de code suffisent à créer un peudo DSL qui simplifie la lecture des tests. Par exemple, pour que les deux lignes de code suivantes soient équivalentes :

result.map(row => row.getString(row.fieldIndex("column_to_test"))).head shouldBe "correct_value"
field("column_to_test") of result.head() shouldBe "correct_value"

Les quelques lignes suivantes permettent cette syntaxe :

def field(name: String) = FieldMatcherOfRow(name)

case class FieldMatcherOfRow(fieldName: String) {
  def of(row: Row) = row.get(row.fieldIndex(fieldName))
}

Ainsi, la lecture se fait de façon proche du langage naturel : « Le champ « column_to_test » de la première ligne de mon DataFrame doit ĂŞtre « correct_value » ». Il faut ensuite laisser libre court Ă  son imagination pour crĂ©er d’autres contenus de ce type afin de rendre lisible tous les tests !

Simplifier l’Ă©criture des tests Spark en Python

Afin de simplifier Ă©galement l’Ă©criture de ces mĂŞmes tests Spark en Python, le manque de DSL est encore plus flagrant. En gĂ©nĂ©ral le framework PyHamcrest propose, selon mes critères (très subjectifs donc), la syntaxe la plus lisible et comprĂ©hensible. Ă€ l’instar de la version Scala, il est possible de crĂ©er des DSL qui rendent nos tests lisibles en Python. L’un des Ă©lĂ©ments importants Ă  tester sur un DataFrame est sa taille. La matcher PyHamcrest correspondant s’Ă©crit de la manière suivante :

class DataFrameCountMatcher(BaseMatcher):

    def __init__(self, expected_count):
        assert isinstance(expected_count, int), 'Provided count is not an int but %s' % type(expected_count)
        self.expected_count = expected_count

    def _matches(self, item):
        if not isinstance(item, DataFrame):
            return False
        return item.count() == self.expected_count

    def describe_to(self, description):
        description.append_text('Given DataFrame has count %d' % self.expected_count)

    def describe_mismatch(self, item, mismatch_description):
        if isinstance(item, DataFrame):
            mismatch_description.append_text('has count %d' % item.count())
        else:
            mismatch_description.append_text('%s is not a DataFrame' % type(item))


def has_count(expected_count):
    return DataFrameCountMatcher(expected_count)

Ce matcher est ensuite très facilement utilisable et produit une syntaxe claire :

class TestDataFrameCountMatcher(unittest.TestCase):

    def setUp(self):
        self.df = sql_context.createDataFrame([
            Row(key='value'),
            Row(key='value2')
        ])

    def test_has_the_right_count(self):
        assert_that(self.df, has_count(2))
Les tests sont l’affaire de tous

La prochaine fois qu’un collègue vous donnera une fausse bonne excuse pour ne pas Ă©crire ses tests en Spark, rappelez-lui que les tests sont l’affaire de tous et qu’il est possible d’Ă©crire des tests simples, maintenables et indĂ©pendants en utilisant ce framework.

Maintenant, même nos amis Data Scientists pourront écrire leurs tests unitaires !

Le prochain et dernier article de cette série traitera des tests de Spark en utilisant le Property Based Testing.

PROCHAIN ARTICLE, bientĂ´t

Tester du code Spark avec du Property Based Testing

Catégories: Blog Société

Le demi-cercle (épisode 3 — Communication Breakdown)

The conclusion seems inescapable that at least with certain kinds of large programs, the continued adaption, modification, and correction of errors in them, is essentially dependent on a certain kind of knowledge possessed by a group of programmers who are closely and continuously connected with them.
Peter Naur – Programming as Theory Building

Tu regardes cette partie du code, Ă  laquelle il faut ajouter une nouvelle fonctionnalitĂ©, et tu t’entends dire tout haut « Je n’aurais jamais Ă©crit ça de cette façon… Comment peut-on faire un design aussi tordu ? » JĂ©rĂ©mie, l’auteur de ce design se trouve Ă  dix pas.  Il est actuellement en train de travailler sur une autre partie du programme, son casque sur les oreilles. Tu pourrais te lever, l’interrompre dans son travail et lui poser des questions, lui faire un retour Ă  propos de ce design. Mais tu te dis « Ă  quoi bon ? » Ce sera au mieux un dĂ©saccord de plus, une occasion supplĂ©mentaire de mesurer la distance qui vous sĂ©pare sur le plan des connaissances, prĂ©fĂ©rences et expĂ©rience en matière de design logiciel. Tu te rappelles qu’il y a dix jours il est venu te voir pour t’annoncer qu’il avait dĂ©cidĂ© de rĂ©Ă©crire entièrement le module de communication Front / Back.

– Ah bon !?
– Je ne comprends rien Ă  ce qui est fait dans ce module.
– Et donc tu veux le rĂ©Ă©crire pour mieux comprendre ?
– Exactement.
– …
– Ce sera plus simple.
– C’est Ă  tes risques et pĂ©rils.
– C’est OK avec la P.O. J’ai fait une U.S. technique.
– Alors c’est OK pour moi. Je ne suis pas en charge de la coordination dans ce projet.

Tu reviens mentalement sur cette conversation. Tu aurais pu insister un peu. Mentionner, par exemple, que tu es ici depuis dix mois, mais qu’il t’en a fallu six avant de pouvoir coder une demande d’Ă©volution sans avoir Ă  demander de l’aide Ă  quatre personnes diffĂ©rentes. Tu te demandes quelles sont les chances que JĂ©rĂ©mie reste encore un an sur le projet.

Tu marches dans une pièce obscure encombrée d’un fatras indescriptible. Tout ton travail consiste à tenter de décrire ces obstacles que tu rencontres. Ce n’est certes pas une description très fiable, puisque tu vois principalement avec les mains, les genoux, parfois la tête. Décrire correctement demande de la pondération. Du calme. Comment rester calme quand on se prend continuellement en pleine face des objets non identifiés ? Mais tu crois qu’en les décrivant, tu ménages le temps de tes coéquipiers, puisque tu leur prépares la voie. En quelque sorte.

– J’ai constatĂ© que lorsque l’utilisateur passe par le menu Administration pour Ă©tablir des autorisations, le template de service n’est pas le mĂŞme que lorsqu’il saisit l’autorisation au vol pendant la transaction.
– Tu veux parler de la transaction 2L ?
– Pas la 2L, la 2LB.
– Tu m’en diras tant. Bienvenue dans le b… Bon courage!
– OK.

Tu pourrais ouvrir ton cahier à n’importe quelle page et intituler cette page : découverte inattendue numéro …

Tu marches dans une pièce obscure, qui n’est pas la même pièce obscure que celle qu’explore Jérémie en ce moment; ni la même que celle de n’importe lequel de tes coéquipiers en fait. Les objets ne sont pas les mêmes; ils n’ont pas la même forme; ils ne sont pas de même nature. Quelles sont nos chances de produire une application cohérente, intègre, robuste dans ces conditions ? Déployés chacun dans sa pièce (est-ce seulement le même bâtiment ?), chacun sur une partie du système, avec quelques outils de survie en milieu obscur, et une bande passante tellement petite que ça en devient ridicule.

Jérémie ne comprend pas cette histoire de bande passante.  Il affirme qu’on n’a jamais eu une fibre aussi puissante.
– Je te parle de la bande passante entre nous, pas du rĂ©seau. C’est une mĂ©taphore.
– Je pige pas tes mĂ©taphores. Sois plus clair.
– VoilĂ .

Maria, qui est PO/Coordinatrice/Chef de Projet est bien d’accord avec le constat, mais elle dit qu’il faut s’y faire. Ce qui compte, c’est ce que voit le client. Tu te demandes comment le client pourrait voir dans ce système un assemblage cohérent d’éléments conçus pour bien fonctionner ensemble, là où tu ne vois qu’un tas d’objets disparates, un agrégat de trucs disséminés, cristallisés, pour certains fossilisés, et que les vagues technologiques successives ont progressivement accumulé en couches. Tu songes à ce que diraient les clients à propos du temps que demande chaque ticket, s’ils pouvaient voir cette coupe archéologique. Où passe leur budget. Quand on voit le prix des choses. Peut-être bien qu’ils nous diraient :

Pourquoi un champ aussi peu fertile, avec un sous-sol aussi riche ?

« Je ne suis pas en charge de la coordination dans ce projet ». Gna gna gna gna. Mais quelle réponse débile. Bien sûr, que Maria va porter la coordination fonctionnelle et technique de ce projet, assurer sa cohérence interne et externe, et en plus elle va tenir ses délais, économiser, veiller à la bonne humeur sur le plateau, et faire grandir son équipe. C’est certain.

Dix heures trente. Tu passes voir si Audrey et Farid prendraient un café.
– C’est pas ma journĂ©e, dis-tu.
– Qu’est-ce qu’il t’arrive ? demande Audrey
– Panne de communication.

Farid sourit et dit:
– Oh. Quand c’est comme ça, je me cale dans mon siège pour la journĂ©e et je me concentre sur un petit truc bien technique.
– En parlant de communication, dit Audrey, je suis allĂ©e au Dojo de programmation jeudi dernier.
– Comment c’Ă©tait ?
– Pas mal. Un des participants a proposĂ© de faire du Mob Programming.
– Connais pas.
– C’est comme le Dojo habituel, avec rĂ©tro-projecteur et tout, Ă  une diffĂ©rence près.
– Laquelle ?
– La personne qui est au clavier attend que les participants lui indiquent ce qu’elle doit coder.

JĂ©rĂ©mie, qui s’est joint Ă  la conversation, dit en vidant une vieille boule Ă  thĂ© dans la poubelle:
– Argh. Je pourrais attendre longtemps..

Farid s’esclaffe.
– Sans rire, dit Audrey, il y a mĂŞme un principe, pour toi, qui aimes bien les principes..

Tu dis:
– Ça m’intrigue. Dis m’en plus.
– C’est le principe Driver/Navigator. Je ne me souviens plus de la phrase exactement. Mais je l’ai notĂ©e.

Vous retournez Ă  vos postes. Audrey ouvre un cahier qui semble aussi rempli que le tien de notes, de listes, et de gros points d’exclamation et de BdMs.

VoilĂ :
Pour aller de la tĂŞte de quelqu’un jusqu’Ă  l’ordinateur, toute idĂ©e doit passer entre les mains de quelqu’un d’autre.

– Llewellyn Falco.

Et on appelle ça, le modèle Driver-Navigator. Le driver est celui qui est au clavier, les navigateurs sont ceux qui réfléchissent, décident, font le design. Tout le monde travaille sur la même Story, devant un écran.

Hmm.

Driver/Navigator.
Driver/Navigator.

Tu reviens sur ton bout de code. De toute évidence, ça changerait un peu la vie dans les labyrinthes obscurs. Tu imagines le groupe en action.
– Pas par lĂ ; il y a une classe entièrement copiĂ©e-collĂ©e sur la classe TransactionBL. J’ai vu ça la semaine dernière.
– Alors contourne, en renommant la classe et en ne gardant que ce qui compte.
– Comment savoir ce qui compte ?
– Moi je sais ! Va dans le module Livraison. Je peux te montrer.
– TransactionBL ? Vraiment ? Il serait temps qu’on se mette d’accord sur les règles de nommage..

Tu fais mentalement la liste des inconvĂ©nients possibles d’un tel choix d’organisation. Maria aurait immĂ©diatement un problème de budget, et viendrait vous faire remarquer que c’est très bien de communiquer Ă  propos du code, mais qu’il ne faut pas rĂŞver. Elle qui accuse dĂ©jĂ  nos rĂ©trospectives de « cramer du gaz »…

En parlant du loup, voilà Maria qui débarque sur le plateau. Et elle vient te voir directement.

– Comment ça se passe ?
– Comme ci, comme ça. Je ne suis pas sĂ»r de finir ce ticket d’ici vendredi.
– Ça n’arrange pas mes affaires. J’ai besoin de montrer quelque chose qui marche.
– Je pense bien…
– Est-ce que tu crois qu’il faut ajouter des ressources au projet ?
– Je ne ferais pas ça Ă  ta place… Ajouter des ressources Ă  un projet…
– … En retard le met encore plus en retard. Je sais. Brooks. C’est quoi le problème en l’occurrence ? Qu’est-ce qu’il faudrait pour que ce ticket passe comme une lettre Ă  la poste ?
– Hum. Revoir l’ensemble du design du produit, et recommencer sur des bases plus saines, avec un standard d’Ă©quipe ?
– Quand tu seras revenu sur terre, rĂ©ponds Ă  ma question s’il te plaĂ®t, dit Maria en souriant.
– Alors, joker. J’ai des problèmes systĂ©miques.
– C’est-Ă -dire ?
– Comme quand tu te rends Ă  la confĂ©rence sur le rĂ©chauffement climatique, et que tu y vas en voiture, tu vois ?
– Pas du tout.
– Ă€ court terme, je peux rĂ©soudre ce ticket, mais en crĂ©ant des problèmes supplĂ©mentaires…
– RĂ©sous le ticket. Pour le reste, on verra plus tard.

De retour dans le code. Dans le code jusqu’au cou. Tu rĂ©flĂ©chis Ă  ce que vous pouvez faire. Vous ne pouvez pas rĂ©Ă©crire intĂ©gralement le système, en dĂ©cidant et en appliquant des principes agrĂ©Ă©s par toute l’Ă©quipe, cela prendrait des semaines de rĂ©unions laborieuses, houleuses, infructueuses.

Englués, chacun dans son dédale obscur, à se cogner dans des trucs.
Impossible de produire un travail d’Ă©quipe, qui se verrait dans le produit.

Tu notes le principe dans ton cahier.
Pour aller de la tĂŞte de quelqu’un jusqu’Ă  l’ordinateur, toute idĂ©e doit passer entre les mains de quelqu’un d’autre.

Tu notes deux conséquences que tu trouves intéressantes:
– De cette manière, vous n’implĂ©menteriez jamais une idĂ©e sans l’avoir d’abord communiquĂ©e.
– De cette manière, vous ne perdriez pas de temps Ă  discuter sur ce qui ne sera pas implĂ©mentĂ©.

Tu décides que jeudi prochain, tu iras au Dojo de programmation.
Tu retournes Ă  ton Ă©cran. Tu enfiles tes Ă©couteurs et tu remets Led Zep’, trop fort.

Catégories: Blog Société

Revue de Presse Xebia

Revue de Presse Xebia
La revue de presse hebdomadaire des écosystèmes Java/JEE proposée par Xebia

Craftsmanship Java: Free at last http://blog.xebia.fr/author/dortegahttp://twitter.com/dicaormuhttp://github.com/dicaormuPar Diana Ortega

Mike Milinkovich directeur exĂ©cutive de la fondation Eclipse a Ă©crit dans son blog « Java: Free At Last », oĂą il analyse une partie des annonces faites dĂ©but septembre par Mark Reinold et Donald Smith d’ Oracle.

Les annonces parlent de l’implĂ©mentation de releases pĂ©riodiques du jdk, mais Milinkovich se focalise sur le dĂ©tail que java deviendrait finalement « free ».

Aujourd’hui les binaires java sont distribuĂ©s sous la licence « Oracle Binary Code License », mais Oracle a l’intention de les mettre directement avec l’OpenJDK sous licence GPL.

Pour plus d’information vous pouvez voir l’article de Micke Milinkovich.

 

 

Back Server-Sent Events

Dans sa version 5, Spring nous propose une alternative aux web-sockets pour notifier le front des changements coté server : les Server-Sent Events (ou SSE).

Cet article nous présente que sont les SSE et nous explique comment les mettre en place dans votre application Spring.

Node.js 8.5 http://www.gravatar.com/avatar/1e0ca9963bcd96ba434e5e4ffd972c2fhttp://blog.xebia.fr/author/%40aletaxin1http://twitter.com/ModuloMhttps://github.com/ModuloMPar Antoine Le Taxin

Une nouvelle release de Node.js vient de sortir !
Principaux ajouts Ă  cette version :

  • La prise en charge native des imports de modules ESM grâce au tag –experimental-modules.
  • L’implĂ©mentation d’une Performance Timing API.
  • La possibilitĂ© d’avoir accès, via la stack, aux traces des Ă©vènements async. 
  • Une mĂ©thode qui permet de directement de copier des fichiers grâce au module fs.

Pour le détail, voir le change log complet, ou une revue par Rising Stack des principales features avec des exemples.

 

Catégories: Blog Société

Jira JQL functions: the ultimate guide

Le blog de Valiantys - jeu, 09/14/2017 - 14:00

If you are a Jira user or administrator, you probably already tried using the search feature, either to look for a specific issue or a list of issues. It can range from being as simple as selecting an option from a drop-down list to a feat that is very complex and confusing and requires knowledge ...

The post Jira JQL functions: the ultimate guide appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Zeenea : exploitez enfin vos Data Lakes !

header zeenea

logo zeeneaPour devenir une organisation “data-driven”, vos collaborateurs ont besoin d’un accès simple et rapide à des données organisées, de bonne qualité et sécurisées.

Afin d’accompagner cette Ă©volution, et forts d’expĂ©riences rĂ©ussies chez nos clients ces 7 dernières annĂ©es dans des missions Big Data, nous avons dĂ©veloppĂ© une solution autour de l’exploitation de Data Lakes. Cette solution a aujourd’hui pris son indĂ©pendance pour devenir Zeenea 

Catégories: Blog Société

Valiantys Wins Atlassian Partner of the Year 2017: Enterprise

Le blog de Valiantys - mer, 09/13/2017 - 07:01

During the Atlassian Summit in San Jose, California, Atlassian announced that Valiantys has won Atlassian Partner of the Year 2017, Enterprise for their outstanding contribution and achievements during Atlassian’s fiscal year 2017. Valiantys was one of 12 recipients honoured as Partner of the Year during the Atlassian Partner Awards ceremony, an annual event to celebrate ...

The post Valiantys Wins Atlassian Partner of the Year 2017: Enterprise appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Meetup PerfUG : Détecter et résoudre vos problèmes de performance applicative avec AppDynamics

AppDynamics vous aide Ă  monitorer vos applications mĂ©tier critiques en Ă©tant plus proactif, en rĂ©duisant votre MTTR, en diminuant le nombre d’incidents en production et en augmentant la visibilitĂ© sur le mĂ©tier et ainsi le revenu online.

AppDynamics dĂ©tecte automatiquement et surveille les performances des transactions mĂ©tier de bout en bout, depuis l’utilisateur web ou mobile jusqu’Ă  la base de donnĂ©es, identifie les lignes de code ralentissant les applications et gère intelligemment vos alertes en apprenant le comportement normal de vos mĂ©triques et en alertant quand elles dĂ©vient de la normale.

Venez assister Ă  une dĂ©monstration interactive de la solution et devenez une rock-star de l’APM en quelques heures.

JB Marzolf est consultant avant-vente depuis 3 ans chez AppDynamics et spĂ©cialiste dans les solutions de gestion de l’IT depuis 15 ans.

Inscriptions et informations sur Meetup. Cette session sera suivie d’un pot dans les locaux d’OCTO.

logo

Le PerfUG est un meetup parisien qui a pour objectif d’offrir un lieu d’échanges informels oĂą toutes les personnes intĂ©ressĂ©es par l’optimisation et la performance sont les bienvenues quel que soit leur niveau. Nous sommes convaincus que la performance est une feature implicite et non nĂ©gociable d’une application et pourtant bien souvent oubliĂ©e. Le PerfUG permet d’Ă©changer idĂ©es et pratiques sur ces sujets pour obtenir plus simplement des systèmes performants. Le PerfUG souhaite faciliter la diffusion des derniers outils et des meilleures techniques pour maĂ®triser au plus tĂ´t la performance d’un système informatique.

imgres

Pour en apprendre davantage sur la Performance, retrouvez notre formation OCTO Academy : Performance des applications et du SI Ă  l’ère du digital

Articles suggested :

  1. PerfUG : Programmation Lock-Free, les techniques des pros
  2. Second anniversaire du PerfUG : Nginx et JVM Off-Heap (Architecture NUMA)
  3. Meetup PerfUG : Couchbase Performance

Catégories: Blog Société

Ethereum : trucs et astuces concernant la machine virtuelle

Les diffĂ©rentes technologies de Blockchain (Bitcoin, Ethereum, etc.) consistent Ă  produire un consensus, entre de nombreuses parties, sur un Ă©tat stable. Pour Bitcoin, il s’agit de se mettre d’accord sur l’état d’un livre de compte. Ethereum ajoute la notion de consensus sur l’état de contrats dits intelligents (smart contracts).Alors qu’un contrat est usuellement contrĂ´lĂ© par une entitĂ© centralisĂ©e, Ethereum permet lui l’exĂ©cution de contrats numĂ©riques sous le contrĂ´le du rĂ©seau distribuĂ©. Des fonds (en monnaie Ă©lectronique Ether) peuvent ĂŞtre sous la responsabilitĂ© du contrat, et le code dĂ©cide ensuite de leur distribution. Typiquement, le contrat peut servir de notaire, en gardant des fonds jusqu’Ă  ce qu’une condition nĂ©cessaire soit validĂ©e (dĂ©lai, preuve de livraison, etc.).

Pour arriver Ă  un consensus sur l’état de la blockchain, des serveurs (que l’on appelle des mineurs) valident chaque nouvel Ă©tat, constituĂ© d’un nouveau bloc, avant son ajout Ă  la suite des prĂ©cĂ©dents. Cette validation passe par la rĂ©solution d’un problème mathĂ©matique complexe consistant Ă  trouver une valeur particulière Ă  associer au bloc, pour rĂ©pondre Ă  certaines caractĂ©ristiques. Le premier serveur Ă  valider un bloc est habilitĂ© Ă  l’ajouter Ă  la chaĂ®ne de bloc. Il est rĂ©munĂ©rĂ© pour cela.

Les développeurs de contrats utilisent généralement le langage de développement Solidity : le code Solidity est compilé dans un byte-code destiné à la machine virtuelle Ethereum (EVM).

Il existe de nombreuses implémentations de la machine virtuelle Ethereum. Certaines simulent les instructions localement dans un navigateur, avec un simple code JavaScript. D’autres convertissent le code des contrats en code assembleur, afin de réduire le temps d’exécution du programme.

Nous vous proposons d’étudier quelques spécificités de l’EVM et du langage Solidity, afin de pouvoir les utiliser judicieusement. L’objectif de cet article est de parcourir les spécificités de cet environnement d’exécution. Des choix ont été faits par les concepteurs d’Ethereum qui sont inhabituels par rapport aux approches utilisées par d’autres langages de programmation. Par exemple, contrairement à Java, la machine virtuelle n’a pas connaissance de notions complexes comme l’héritage, le polymorphisme, les constructeurs, la gestion mémoire, etc. Nous pensons que connaître ces spécificités permet de proposer des smart contrats plus efficaces. Dans un prochain article, nous mettrons à profit ces particularités pour contourner une limite d’Ethereum : l’immuabilité des contrats.

1 Ethereum VM (EVM)

L’Ethereum Virtual Machine propose des calculs sur des données de 256 bits (32 bytes) à l’aide d’une pile d’exécution. Les valeurs sont déposées sur une pile et les instructions consomment les données au-dessus de la pile pour y déposer le résultat.

Par exemple, la suite d’instructions suivante effectue une addition et place le résultat sur le sommet de la pile.

PUSH1 0x42
PUSH1 0x24
ADD

Il y a également des zones mémoires spéciales. La RAM, dédiée à l’exécution de la transaction, commence à l’adresse zéro et augmente au fur et à mesure des besoins, par pas de 32 bytes. Comme toutes les instructions de la machine virtuelle, cela a un coût (1 gaz par 32 bytes écrit).

Les données persistantes dans le contrat (c’est à dire les attributs) sont mémorisées dans une table d’association utilisant 32 bytes pour la clé et 32 bytes pour la valeur. Les modifications sont sauvegardées dans le prochain bloc de la blockchain, après validation par la communauté.

Un attribut coûte 20.000 gaz lors de sa création (passage à une valeur différente de zéro), puis 5000 gaz lors de chaque modification. Il rapporte 15.000 gaz lors de sa remise à zéro.

Pour invoquer une méthode d’un contrat, il faut envoyer une transaction au réseau Ethereum. Charge à lui de trouver le mineur qui sera en capacité d’exécuter la méthode. Cela se traduit techniquement par l’envoi d’une transaction au réseau, avec la description de l’invocation (contrat, méthode, paramètres).

Dans une transaction, on trouve :

  • msg.sender : l’Ă©metteur du message (current call)
  • msg.gas : Le gaz maximum restant pour la suite du traitement
  • msg.data : Les paramètres de l’invocation du contrat
  • msg.sig : La signature de la mĂ©thode invoquĂ©e, sur 4 bytes. Il s’agit en fait d’un calcul de hash sur le nom de la mĂ©thode, intĂ©grant la liste des paramètres. Cela correspond aux quatre premiers octets de msg.data
  • msg.value : Ă©ventuellement, une quantitĂ© de wei (sous unitĂ© d’éther) pour transfĂ©rer des fonds au contrat

Pour construire un contrat depuis l’extérieur de la blockchain, il faut invoquer le contrat zéro, avec en paramètre, le code et la description du nouveau contrat. Le code du contrat zéro se charge d’ajouter le nouveau contrat dans la chaîne de bloc, d’exécuter le constructeur et de retourner l’adresse du contrat.

Pour construire un contrat depuis un autre contrat, il est possible d’utiliser l’instruction CREATE de l’EVM. Cela est moins coûteux que depuis l’extérieur (100 gaz contre 53.000, cf les spécification de l’EVM).

Des instructions permettent à un contrat d’écrire des logs. Les logs sont des tableaux de bytes, associés à chaque contrat. Cela permet aux API externes de récupérer des informations lorsqu’une transaction est validée par la communauté. C’est le canal privilégié par Solidity pour les communications asynchrones entre les contrats et l’extérieur d’Ethereum. Ecrire dans un log permet de communiquer le résultat d’un appel asynchrone.

Parmi les instructions avancées de la machine virtuelle Ethereum, il y a trois instructions spéciales :

  • call pour invoquer un autre contrat et le modifier
  • callcode pour utiliser le code d’un autre contrat, sur l’état du contrat appelant, en indiquant le contrat actuel comme Ă  l’origine de l’invocation.
  • delegatecall pour utiliser le code d’un autre contrat, sur l’état du contrat appelant, en gardant la valeur du msg.sender (l’identitĂ© de l’invocation de la mĂ©thode d’origine).

callcode n’est pas très utile Ă  cause du changement de msg.sender. Pour ne pas casser la compatibilitĂ©, ils ont ajoutĂ©s une nouvelle instruction : delegatecall.

2 Solidity

Au-dessus de la machine virtuelle, le langage Solidity propose de nombreux concepts de plus haut niveau, pouvant être compilés en instructions de la machine virtuelle. Souvent, le code rédigé en Solidity est très éloigné du code compilé pour l’EVM. Il est parfois important de comprendre la relation entre les deux, pour exploiter au mieux les avantages des deux modèles de programmation.

Petit rappel sur les concepts portés par Solidity :

  • GĂ©nère du code EVM
  • propose une sorte d’hĂ©ritage avec la notion de constructeur
  • propose un modèle particulier de polymorphisme

• Comme les instructions de la EVM fonctionnent en 256 bits, cela n’Ă©conomise pas le coĂ»t de sauvegarde des informations. Pour optimiser cela, Solidity se charge d’effectuer des opĂ©rations de masque binaire, pour pouvoir manipuler quelques bytes Ă  la fois sur chaque zone mĂ©moire de 32 bytes. Par exemple, la mĂ©thode f() suivante :

contract Test {
  byte b;
  function f() {
    b=0xAA;
  }
}

est compilée en cette longue suite d’instructions. Tout cela pour ne modifier QUE le premier octet des 32 bytes.

// b = b AND NOT(0xFF) OR 0xAA
PUSH 0 // Offset de l’attribut
DUP1
SLOAD // Lecture de l’attribut de position 0
PUSH FF // Masque binaire pour un octet
NOT // Inversion du masque (0xFFFFF....FF00)
AND // Masque entre la donnée de l’attribut et le masque
PUSH AA // Valeur Ă  Ă©crire
OR // Or entre 0xAA et l’attribut moins le premier octet
SWAP1
SSTORE // Ecriture de l’attribut

Cela fait beaucoup d’instructions, mais reste bien moins coûteux en éther que de mémoriser chaque octet dans un espace mémoire de 32 bytes.

• Comme il n’y a qu’un seul point d’entrée pour un contrat EVM, Solidity a décidé de consacrer les quatre premiers octets des paramètres de la transaction à l’identification de la méthode à invoquer. La valeur des octets correspondent à un bytes4(sha3("f(uint)") sur le nom de la méthode, complété par le type des différents paramètres.

Le code du contrat commence par un aiguillage semblable à un switch, basé sur la signature de chaque méthode. Si aucune méthode ne correspond, alors la méthode par défaut est utilisée. C’est une méthode particulière, n’ayant pas de nom ni de paramètre. Cette approche permet de gérer le polymorphisme.

switch (msg.sig) {
  case e1c7392a : // function init()
  …
  case 2db12ac4 : // function changeToV2()
  …
  case 82692679 : // function doSomething()
  …
  default : // function ()
  …
}

• La machine virtuelle n’a pas de notion de constructeur. Pour construire une instance, il faut envoyer une transaction vers le contrat zéro. Les datas sont alors considérées comme du code à exécuter.

Solidity génère automatiquement le code du constructeur, qui se charge de fournir le code du contrat à générer sous forme de data.

• Solidity propose une instruction throw, pour signaler une erreur dans un contrat. Comme il n’existe pas d’équivalent dans la machine virtuelle, le code généré invoque un saut vers une adresse mémoire invalide. Cela implique une erreur lors de l’exécution, ce qui est le but recherché. Ne vous étonnez pas alors, de recevoir une erreur de type “invalid jump destination”.

• Solidity propose la notion d’event, qui correspond aux logs de la machine virtuelle. Ils permettent d’informer un code Ă  l’écoute de la chaĂ®ne lorsqu’une mĂ©thode d’un contrat est invoquĂ©e. Des filtres permettent d’attendre l’acquittement d’un traitement particulier dans la blockchain. Concrètement, Solidity utilise toujours la première valeur des logs de l’EVM pour y placer le hash de la signature de l’event. Les autres valeurs correspondent aux paramètres de l’event. Le hash de la signature de l’évènement est utilisĂ© comme nom de topic, pour l’indexation des Ă©vĂ©nements.

• Un dernier point à savoir. Pour maîtriser la mémoire nécessaire à l’invocation des méthodes, l’attribut de clé 0x40 est utilisé par Solidity. La valeur stockée indique la plus haute adresse mémoire en mémoire RAM utilisée par les méthodes du contrat.

Dans un prochain article, nous utiliserons toutes ces particularités pour répondre à la question suivante : comment modifier un contrat immuable ?

Articles suggested :

  1. Introduction Ă  la technologie Blockchain
  2. Une cagnotte dans la blockchain Ethereum
  3. Raiden, une réponse à la confidentialité des échanges sur Ethereum ?

Catégories: Blog Société

EMR : Instancier son cluster Hadoop simplement

Logo Amazon EMR

Amazon Elastic MapReduce (EMR) est le service qui simplifie la création et la mise à disposition d’une stack Hadoop complète et ajustable. Grâce à ce service, il suffit de quelques opérations pour créer un cluster Hadoop dans le Cloud embarquant les principaux outils de l’écosystème tels que YARN, Spark, Zeppelin, Hive, Presto, HBase et HDFS. Cet article propose de faire un tour d’horizon de ce service et de quelques moyens d’en profiter. Il s’inscrit dans une série d’articles autour de l’instanciation de clusters dans le Cloud.

Nous prĂ©senterons les raisons qui peuvent pousser Ă  utiliser EMR, puis nous parlerons brièvement des diffĂ©rents services Amazon impliquĂ©s. Ensuite, après avoir dĂ©terminĂ© l’architecture cible, nous passerons en revue les questions qu’il est indispensable de se poser, puis nous crĂ©erons notre cluster.

Pourquoi utiliser EMR ?

Il est intéressant d’utiliser EMR pour limiter le coût de l’infrastructure nécessaire à l’exécution des services Hadoop. Dans la plupart des cas, l’infrastructure englobant l’écosystème Hadoop n’est utile que durant la phase de processing de la donnée. Une fois le traitement terminé, l’infrastructure peut être détruite.

Voici la chaîne d’opérations à réaliser dans le cadre de traitements de type batch.

  1. Stocker les données dans le Cloud.
  2. Instancier un cluster Hadoop.
  3. Traiter les données au sein du cluster.
  4. Produire de nouvelles données ayant de la valeur.
  5. Stocker les données utiles résultantes dans le Cloud.
  6. DĂ©truire le cluster.
  7. Exploiter les résultats.

La solution EMR est flexible en terme d’infrastructure, de distribution Hadoop, de produits logiciels inclus et de durée de vie du cluster. De plus, le monitoring du cluster est inclus avec CloudWatch. Pour finir, EMR offre une interaction avec l’ensemble des autres services AWS.

L’utilisation d’EMR n’est pas restreinte Ă  des chaĂ®nes d’utilisation de type batch. EMR est capable de consommer un nombre important de ressources variĂ©es, ce qui ouvre la porte Ă  de nombreux usages.

Pré-requis AWS

EMR fait intervenir un ensemble de services AWS qu’il est bon d’introduire.

Amazon Virtual Private Cloud (VPC)

Tous les noeuds EMR s’exécutent dans un VPC au sein d’un sous réseau d’une zone de disponibilité (AZ). Pour rappel, en fonction d’une zone géographique où le service AWS est disponible comme par exemple en Europe (Dublin et bientôt Paris), il existe deux à trois zones de disponibilité (AZ). Pour y voir plus clair sur les VPC et avoir une vision complète, rien de tel que l’article de mon collègue Jeremy Pinsolle.

Identity and Access Management (IAM)

C’est le service qui permet la gestion des identitĂ©s et des accès. Ce service pourrait faire l’objet d’un article Ă  lui tout seul ! Pour rester sur notre sujet EMR, nous allons retenir que pour instancier le cluster EMR via API REST, il est nĂ©cessaire d’activer une clĂ© d’API dans IAM pour notre compte utilisateur AWS.

IAM permet d’attacher des stratégies de sécurité (policies) relatives à l’utilisation des services. Chaque stratégie est identifiable via un nom unique Amazon Resource Name (ARN) et peut être associée à des utilisateurs ou à des rôles. Ce système d’ARN est utilisé partout sur AWS, où tout est considéré comme une ressource.

Voici un exemple de stratégie permettant à l’utilisateur de changer son propre mot de passe dans IAM :

arn:aws:iam::aws:policy/IAMUserChangePassword

Au sein d’ IAM, des droits sont associĂ©s Ă  cette stratĂ©gie. Dans le cas prĂ©sent, si j’associe cette stratĂ©gie Ă  mon compte utilisateur et que j’ajoute des droits de lecture/Ă©criture, je pourrai Ă©crire ou lire sur le service IAM pour changer mon mot de passe.

Pour EMR, nous allons devoir créer deux rôles dans IAM.

Un premier rôle pour le service EMR : Celui-ci permettra à EMR de créer un subnet, et d’instancier des machines avec EC2.

arn:aws:iam::XXXXX:role/EMR_DefaultRole

Un second rôle pour les instances qui composent le cluster EMR pour qu’elles puissent par exemple récupérer des données sur S3 et consommer des services tiers.

arn:aws:iam::XXXXX:role/EMR_EC2_DefaultRole
EC2

Ce sont les instances de machines qui composent le cluster Hadoop. Il convient de choisir les types d’instances EC2 adĂ©quates au regard du nombre de composants dĂ©ployĂ©s et des performances attendues. Amazon fournit la liste des instances compatibles avec EMR. A titre d’exemple, une m3.xlarge avec ses 4vCPU, 15Go de RAM et 2*40 Go de disque SSD suffit Ă  faire tourner une typologie de cluster Ă  base de Spark, Hadoop, Tez, Hive avec un niveau de performance acceptable. 

S3

Le service de stockage historique est utilisé pour stocker les logs d’EMR, ainsi que les données froides en entrée et en sortie du cluster. Il est enfin utilisé pour stocker les Notebooks Zeppelin si on utilise ce composant. Ajouté à ces usages, on peut l’utiliser pour stocker les données d’HBase  sans HDFS.

Amazon Machine Image (AMI)

Il s’agit des images de machines utilisées pour peupler notre cluster. L’utilisation de l’AMI AWS permet de ne pas ajouter de coûts supplémentaire de licence.

Dynamo DB

Le composant EMR FS, dont l’utilisation permet de s’assurer de la cohérence et de la localisation des données sur S3, utilise Dynamo DB pour stocker en interne ses métadonnées. Avec lui, EMR consomme facilement les données de S3.

Relational Database Service (RDS)

RDS est fortement recommandé pour stocker les données non reconstructibles telles que :

  • les informations du mĂ©ta store Hive
  • les Workflows Oozie
  • les mĂ©tadonnĂ©es de Hue

Grâce à cela, même si j’utilise un cluster éphémère mes métadonnées ne seront pas perdues.

Elastic Block Store (EBS)

Les instances EC2 sont livrĂ©es avec des capacitĂ©s disque diffĂ©rentes. Par exemple, pour des instances de type m3.xlarge, elles reposent sur deux disques SSD de 40 Go. Dans un contexte EMR, une partie de cet espace disque est dĂ©diĂ© au système d’exploitation et le reste est dĂ©diĂ© Ă  HDFS. La capacitĂ© d’HDFS est ajustable via l’ajout d’EBS supplĂ©mentaires aux instances.

Architecture cible 

L’architecture cible choisie est simple et n’aborde pas les aspects sĂ©curitĂ©. Il conviendra d’ajouter ces composants en amont de l’architecture (bastion, Reverse proxy, Key Management Service, Security group…).

La figure ci-dessous présente de manière macroscopique les composants entrant en ligne de compte  :

Architecture cible

On y retrouve les Ă©tapes type d’opĂ©rations que nous allons rĂ©aliser pour un traitement batch :
  • Stockage : Les donnĂ©es sont dĂ©posĂ©es sur S3 avant l’instanciation. Notre cluster EMR log Ă©galement son Ă©tat sur S3.
  • Data management : Toutes les tables crĂ©Ă©es par l’utilisateur dans Hive, par exemple, sont stockĂ©es dans RDS. Hive ou Presto utilisent ce que l’on nomme le schema on read, et par consĂ©quent, peuvent poser un schĂ©ma sur de la donnĂ©e prĂ©sente dans S3.
  • Traitement : EMR consomme S3, et s’aide d’EMR FS pour s’assurer de la data locality en utilisant DynamoDB. DiffĂ©rents types de processing peuvent ĂŞtre rĂ©alisĂ©s via des outils tels que Hive, Spark, Zeppelin, Pig, Presto, HBase. Le cluster EMR expose un ensemble de WebUI.
  • Stockage & exposition : Les rĂ©sultats du traitement sont stockĂ©s dans S3. 
Préparation

Nous allons maintenant nous poser les bonnes questions pour préparer un cluster conforme à nos besoins.

De quelles données ai-je besoin ? Quel niveau de confidentialité ?

Avant d’instancier un cluster pour une utilisation en mode batch, il convient de copier les donnĂ©es utiles dans S3 et d’en permettre l’accès Ă  notre futur cluster EMR pour qu’il les consomme. De nombreuses solutions existent pour amener la donnĂ©e sur S3. Vous en trouverez quelques exemples dans ce retour d’expĂ©rience d’un workshop Big Data. La manipulation de donnĂ©es implique le respect des diffĂ©rentes règles relatives Ă  leur confidentialitĂ© et Ă  leur protection. A titre d’exemple, les mĂ©canismes de chiffrement des donnĂ©es S3 peuvent ĂŞtre activĂ©s. EMR FS supporte ces mĂ©canismes Ă  condition d’en utiliser qu’un seul simultanĂ©ment.

Au niveau europĂ©en, la lĂ©gislation applicable « General Data Protection Regulation » (GDPR) pourra ĂŞtre respectĂ©e dès son entrĂ©e en vigueur en mai 2018, c’est du moins ce Ă  quoi s’engage AWS.

De quelles type d’authentification utilisateur ai-je besoin ? De quelle manière manière dois-je gĂ©rer les autorisations ?

Les mĂ©canismes habituels d’authentification utilisateur comme Kerberos ou encore LDAP peuvent ĂŞtre activĂ©s. Les « Role Based Access Control » (RBAC) avec l’outil Apache Ranger peuvent ĂŞtre implĂ©mentĂ©es ce qui permettra de gĂ©rer finement les accès utilisateur Ă  vos donnĂ©es et de savoir qui y a accĂ©dĂ©.

Quelle distribution choisir ?

Le service EMR embarque par défaut une distribution Hadoop composée des différents outils de l’écosystème Hadoop. La figure ci-dessous extraite de la documentation présente les différentes releases d’EMR avec les versions des outils correspondant :

EMR Releases

Les outils qui composent une release d’EMR sont déclinés en version Open source pure ou en version spécifique pour EMR.

Un exemple illustrant ce point avec la bibliothèque « hadoop command line client » disponible en version 2.7.3-amzn-2 sur EMR. Dans ce cas, il s’agit d’une version spĂ©cifique pour EMR car sa signature est suffixĂ©e « amzn-2 ». Le choix de la distribution est motivĂ© par les versions des composants que nous souhaitons utiliser, leurs disponibilitĂ© au sein d’une release EMR et la stabilitĂ© vĂ©rifiĂ©e de l’ensemble. Il convient de ne pas prendre la toute dernière release d’EMR mais l’une des plus rĂ©centes. Pour conclure ce choix, un cluster EMR peut ĂŞtre Ă©galement instanciĂ© avec diffĂ©rentes versions de la distribution MapR. Le prĂ©sent article ne couvre pas ce mode de fonctionnement.

De quels outils ai-je réellement besoin ?

À partir de la version EMR choisie, il faut sélectionner la liste des outils que nous allons utiliser. Si nous utilisons uniquement les outils nécessaires, notre cluster démarrera beaucoup plus rapidement. Par défaut, la console AWS propose quatre topologies de cluster. Via le client AWS en ligne de commande (aws-cli) ou au sein des options avancées, nous pouvons construire une topologie personnalisée.

La flexibilitĂ© d’EMR permet d’installer un composant Ă  postĂ©riori avec une commande du type :

aws emr install-applications --cluster-id j-2A6HXXXXXXL7J --apps Name=Hive
Ai-je besoin d’un cluster permanent ou éphémère ?

Short long

Nous pouvons lancer deux types de cluster EMR :

Les clusters « long running » sont permanents et ne s’Ă©teignent pas sauf action spĂ©cifique de son propriĂ©taire. Les clusters Ă©phĂ©mères quant Ă  eux rĂ©alisent une ou plusieurs étapes et s’Ă©teignent une fois ces dernières terminĂ©es. Les Ă©tapes sont des mĂ©canismes complets qui peuvent rĂ©aliser un ensemble d’actions comme par exemple un job Spark. On peut aussi ajouter des Ă©tapes sur les clusters « long running ». Historiquement, il existait une limitation Ă  256 Ă©tapes maximum sur un « long running » cluster. Cette règle est maintenant transformĂ©e en 256 Ă©tapes simultanĂ©es maximum sans limitations du nombre total d’Ă©tapes.

Le diagramme ci-dessous extrait de la documentation AWS présente les différents états d’un cluster EMR quelque soit son type.

Aws diagramme

On constatera que ce qui diffĂ©rencie le « long running » de l’Ă©phĂ©mère sur ce diagramme est le fait que le long running reste dans l’Ă©tat WAITING jusqu’Ă  une action de terminaison du cluster. Un cluster Ă©phĂ©mère quant Ă  lui rĂ©alise ses Ă©tapes et passe ensuite au statut SHUTTING_DOWN puis COMPLETED.

Quelle est la volumétrie des données à traiter ? Quel type de traitement vais-je réaliser ?

La volumétrie de données à traiter est un facteur déterminant sur le sizing du cluster. Plus, il y a de données en entrée de mon traitement et plus je vais avoir besoin de ressources si je souhaite que l’ensemble de mon jeu de données soit traité en parallèle et rapidement.

Est-ce que mon traitement est CPU, IO ou Memory bound ? En fonction de ces critères, je vais choisir le type d’instance EC2 qui vont peupler mon cluster.

Un cluster EMR est composé de trois groupes d’instance(s) EC2 :

  • Master : Noeuds maĂ®tres ou noeuds de management. C’est sur ce type de noeud que des processus « Master » tels que le namenode de HDFS ou le ressource manager de YARN s’exĂ©cutent.
  • Core : Ce sont les workers de notre cluster et ce sont eux qui exĂ©cutent le processus DataNode pour HDFS et Node Manager pour YARN.
  • Task : Noeuds de pur calcul, ils peuvent exĂ©cuter des processus de type Node Manager pour YARN mais ne stockent pas les donnĂ©es d’HDFS.

La figure ci-dessous présente un exemple de topologie de cluster EMR incluant des noeuds core et master.

EMR.png

Quel est mon budget ?

Le budget est bien entendu primordial et ce d’autant plus lorsqu’on dĂ©marre une horde de machines EC2. Avant d’utiliser le service EMR, il est bon d’exploiter la calculatrice pour estimer le coĂ»t de facturation en se basant sur le coĂ»t horaire des machines. Il faut aussi tracer la facturation liĂ©e Ă  l’instanciation du cluster en le « taggant ».

Afin de limiter les coûts, rien de tel que les instances ponctuelles, également nommées instances spot. On peut composer un cluster EMR uniquement à base d’instances ponctuelles. Pour ce faire, il faut se renseigner sur le cours historique de l’instance ciblée dans la zone de disponibilité cible. Si on propose un prix légèrement au dessus de l’historique du cours, on peut diviser par 5 le coût de l’instance à l’heure !

Voici une commande Ă  adapter permettant de se renseigner sur le cours d’une instance de type « m3.2xlarge » depuis le 25/07/2017 Ă  7h08 dans l’AZ eu-west-1c

  aws ec2 describe-spot-price-history --instance-types m3.2xlarge  --start-time 2017-07-25T07:08:09 --availability-zone eu-west-1c


Autre facteur de coût : le stockage EBS associé à chaque machine. Hadoop repose historiquement sur HDFS qui lui-même repose sur des disques configurés en JBOD. En environnement AWS EMR, le stockage EBS est utilisé pour stocker les données temporaires de nos phases de processing et également pour HDFS. Nous pouvons associer plusieurs volumes EBS à nos instances, ce qui aura pour effet de démultiplier les I/O pour l’accès à ces données. Quoi qu’il arrive à nos volumes EBS sur EMR, ils sont éphémères et non reconstructibles. C’est également le parti pris par HDFS avec sa gestion des réplicats permettant de s’affranchir de la perte d’un disque ou d’une machine.

Quelle est ma scaling policy ?

Atout considĂ©rable d’EMR, l’augmentation dynamique de la capacitĂ© de traitement du cluster. Nous pouvons augmenter la capacitĂ© du cluster (Scale Out) ou la diminuer (Scale In) en fonction de multiples mĂ©triques du cluster (mĂ©triques remontĂ©es par CloudWatch). Ă€ titre d’exemple, nous pouvons engendrer un Scale Out du cluster si la mĂ©moire disponible dans YARN est infĂ©rieure Ă  15 % du total disponible. Les nĹ“uds master quant Ă  eux ne sont pas scalables, les politiques de scale In/Out sont uniquement rĂ©alisables sur les noeuds de type Core ou Task. CloudWatch va surveiller les mĂ©triques de YARN et agir sur EMR au travers du rĂ´le AmazonElasticMapReduceAutoScaling.

La figure ci-dessous présente la stratégie par défaut pour l’Auto Scaling d’EMR.

Auto Scaling EMR

Les stratĂ©gies de scaling sont cumulables. Il faut par consĂ©quent veiller Ă  ce qu’elles ne se mordent pas la queue !!

Dois-je adapter la configuration des services ?

Toutes les configurations par dĂ©faut des composants Hadoop sont modifiables dès l’instanciation du cluster. Par exemple, nous pouvons modifier les paramètres par dĂ©faut des services Hadoop tels que YARN ou Spark. Ces actions sont possibles directement dans l’aws cli avec l’utilisation de l’option --configuration ou la rĂ©fĂ©rence Ă  un fichier json dĂ©posĂ© au prĂ©alable sur S3. Une troisième mĂ©thode consiste Ă  passer par la console pour modifier les options de configuration des services. Les diffĂ©rents niveaux de configuration des services Hadoop sont modifiables qu’il s’agisse d’un fichier hive-site.xml ou d’une propritĂ© du fichier hue.ini.

Voici quelques exemples d’actions de configuration qu’il est bon de rĂ©aliser :

Premier exemple, configurer Hive pour qu’il utilise une base de donnĂ©es RDS MariaDB ainsi que quelques propriĂ©tĂ©s au sein du fichier hive-site.xml.

{
  "Classification": "hive-site",
  "Properties": {
    "hive.server2.authentication": "NOSASL",
    "hive.enforce.bucketing": "true",
    "hive.support.concurrency": "true",
    "javax.jdo.option.ConnectionUserName": "<DB_USER>",
    "hive.txn.manager": "org.apache.hadoop.hive.ql.lockmgr.DbTxnManager",
    "javax.jdo.option.ConnectionDriverName": "org.mariadb.jdbc.Driver",
    "javax.jdo.option.ConnectionPassword": "<DB_PASSWORD>",
    "hive.exec.dynamic.partition.mode": "nonstrict",
    "javax.jdo.option.ConnectionURL": "jdbc:mysql://<DB_HOST>:3306/hivemetastoredb?createDatabaseIfNotExist=true",
    "hive.compactor.initiator.on": "true",
    "hive.compactor.worker.threads": "1"
  },
  "Configurations": []
}

Ceci aura pour effet de garantir la non perte des métadonnées des tables déclarées dans Hive dans le cas où le cluster disparaît.

Deuxième exemple, mais d’un niveau diffĂ©rent, configurer Hue pour qu’il utilise une base de donnĂ©es RDS MariaDB. Dans le prĂ©cĂ©dent exemple, on configurait les propriĂ©tĂ©s d’un service Hadoop (Hive) dans la zone Properties. Ici, on modifie la zone [desktop] et sa sous zone [[database]] Ă  l’aide du JSON suivant.

{
  "Classification": "hue-ini",
  "Properties": {},
  "Configurations": [
    {
      "Classification": "desktop",
      "Properties": {},
      "Configurations": [
        {
          "Classification": "database",
          "Properties": {
            "password": "<DB_PASSWORD>",
            "engine": "mysql",
            "port": "3306",
            "host": "<DB_HOST>",
            "name": "<DB_NAME>",
            "user": "<DB_USER>"
          },
          "Configurations": []
        }
      ]
    }
  ]
}


Un troisième exemple, consiste Ă  configurer Zeppelin pour qu’il stocke les Notebooks dans S3 :

{
  "Classification": "zeppelin-env",
  "Properties": {},
  "Configurations": [
    {
      "Classification": "export",
      "Properties": {
        "ZEPPELIN_NOTEBOOK_S3_BUCKET": "<BUCKET_S3>",
        "ZEPPELIN_NOTEBOOK_S3_USER": "<USER_S3>",
        "ZEPPELIN_NOTEBOOK_STORAGE": "org.apache.zeppelin.notebook.repo.S3NotebookRepo"
      },
      "Configurations": []
    }
  ]
}

Grâce à cette configuration, les Notebooks développés sur Zeppelin seront sauvegardés dans S3.

Actions d’amorçage ?

Les actions d’amorçage sont lancĂ©es avant l’exĂ©cution des traitements sur le cluster et sont optionnelles. Elles permettent entre autre, d’installer des paquets supplĂ©mentaires sur les machines, d’ajouter des utilisateurs, … Un ensemble de scripts stockĂ©s sur S3 et appelables en tant qu’action d’amorçage est mis Ă  disposition par AWS.

Let’s go

Nous sommes maintenant prĂŞts pour dĂ©marrer un EMR . Cette action peut se faire de diffĂ©rentes manières : pour l’illustrer nous allons prendre un besoin de cluster EMR fictif dĂ©taillĂ© dans le tableau ci-dessous :

CHOIX A REALISER CHOIX EFFECTUE Type de cluster Long running Release EMR emr-5.7.0 Sous réseau subnet-XXX Groupe de sécurité du master sg-XXXX Groupe de sécurité des machines core sg-XXXX Outils Hadoop

Hue

Spark

Hive

Zeppelin

HCatalog CaractĂ©ristiques du groupe d’instances master 1 instance m3.xlarge de type ponctuelle avec un prix horaire souhaitĂ© de 0.10$ CaractĂ©ristiques du groupe d’instances core 2 instances m3.xlarge de type ponctuelle avec un prix horaire souhaitĂ© de 0.10$ Configuration de EMR FS

fs.s3.consistent.retryPeriodSeconds Ă  10 secondes
fs.s3.consistent Ă  true
fs.s3.consistent.retryCount Ă  5
fs.s3.consistent.metadata.tableName Ă 
EmrFSMetadata
Auto Scaling Utilisation du rĂ´le EMR_AutoScaling_DefaultRole Actions d’amorçage Aucunes Taille du volume racine EBS 10 Go pour le volume root (ce qui aura pour impact de dĂ©dier le reste Ă  HDFS). RĂ´le pour le service EMR dans IAM EMR_DefaultRole RĂ´le pour les instances EC2 dans IAM EMR_EC2_DefaultRole Nom du cluster my-first-emr Contrainte de scale Down TERMINATE_AT_INSTANCE_HOUR RĂ©gion de facturation eu-west-1 RĂ©pertoire de log dans S3
s3n://aws-logs-XXX-eu-west-1/elasticmapreduce/


La commande d’exemple suivante (Ă  adapter) utilisant l’aws cli suffit Ă  dĂ©marrer un cluster :

aws emr create-cluster \
      --applications Name=Hadoop Name=Hue Name=Spark Name=Hive Name=Zeppelin Name=HCatalog \
      --ec2-attributes  '{"KeyName":"bbo_emr","InstanceProfile":"EMR_EC2_DefaultRole","SubnetId":"subnet-XXX","EmrManagedSlaveSecurityGroup":"sg-XXXX","EmrManagedMasterSecurityGroup":"sg-XXX"}' \
      --release-label emr-5.7.0 \
      --log-uri 's3n://aws-logs-XXX-eu-west-1/elasticmapreduce/' \
      --instance-groups '[{"InstanceCount":1,"BidPrice":"0.10","InstanceGroupType":"MASTER","InstanceType":"m3.xlarge","Name":"Groupe d instances maître - 1"},{"InstanceCount":2,"BidPrice":"0.10","InstanceGroupType":"CORE","InstanceType":"m3.xlarge","Name":"Groupe d instances principal - 2"}]' \
      --configurations '[{"Classification":"emrfs-site","Properties":{"fs.s3.consistent.retryPeriodSeconds":"10","fs.s3.consistent":"true","fs.s3.consistent.retryCount":"5","fs.s3.consistent.metadata.tableName":"EmrFSMetadata"},"Configurations":[]}]' --auto-scaling-role EMR_AutoScaling_DefaultRole      \
      --ebs-root-volume-size 10 \
      --service-role EMR_DefaultRole \
      --enable-debugging \
      --name 'my-first-emr' \
      --scale-down-behavior TERMINATE_AT_INSTANCE_HOUR \
      --region eu-west-1


Quand on connaĂ®t la multitude d’opĂ©rations Ă  rĂ©aliser pour mettre en service un cluster Hadoop traditionnel, pouvoir le faire en une commande est exceptionnel. Une fois la commande exĂ©cutĂ©e, le cluster passe par les diffĂ©rents Ă©tats dĂ©crits plus haut. L’Ă©tat du cluster est visible via les diffĂ©rents moyens d’interfaçage avec AWS. Dans le cas d’un long running cluster, les interfaces utilisateur Zeppelin et Hue sont opĂ©rationnelles au bout d’une dizaine de minutes seulement !

Pour y accéder, deux possibilités :

  • Connexion SSH au master et crĂ©ation de tunnels vers les diffĂ©rentes WebUI disponibles.
  • Connexion SSH au master incluant un proxy socks avec dynamic port forwarding.
Une fois cette connexion SSH Ă©tablie, depuis Hue, on peut maintenant accĂ©der aux donnĂ©es de S3 et en produire de nouvelles Ă  l’aide d’Hive. Les possibilitĂ©s sont mutliples et pour citer un dernier exemple, on peut lancer des traitements avec Spark depuis l’interface Zeppelin ou directement en ligne de commande. Voici un exemple de lancement d’un jar Spark (calcul de Pi du jar spark-examples) en ligne de commande sur un cluster « long running » :
aws emr add-steps --cluster-id j-2AXXXXXXGAPLF --steps Type=Spark,Name="Spark Program",ActionOnFailure=CONTINUE,Args=[--class,org.apache.spark.examples.SparkPi,/usr/lib/spark/lib/spark-examples.jar,10]
Infra as Code (IaC)

Le code de l’infrastructure EMR construite peut vivre au sein de vos projets Data et suivre ses propres releases. Il existe plusieurs possibilités pour créer, tester et déclencher le code associé à notre infrastructure.

Voici deux outils bien connus des profils Devops

  • Terraform : Outil multi fournisseurs de Cloud dĂ©veloppĂ© par HashiCorp, il permet de planifier, provisionner et dĂ©truire notre infrastructure dĂ©crite dans des fichiers Ă  l’extension .tf. Il est très complet mais il n’a pas nativement toutes les fonctionnalitĂ©s AWS spĂ©cifiques Ă  EMR. Heureusement, des JIRA sont en cours de rĂ©solution pour les prochaines versions et il est possible de dĂ©velopper ses propres composants. Un exemple de configuration pour EMR est fourni.
  • CloudFormation : IntĂ©grĂ© Ă  AWS uniquement, il permet d’Ă©laborer une infrastructure et de la dĂ©crire sous forme JSON ou YAML. Cette infrastructure « Cloud formĂ©e » peut ĂŞtre ensuite construite via les diffĂ©rents moyens d’interagir avec AWS, mais Ă©galement avec un outil comme Ansible. De par sa disponibilitĂ© au sein de la constellation des services Amazon, CloudFormation interagit facilement avec EMR.
Flexible, facile et complet pour un besoin Hadoop rapide

EMR est une solution très flexible que l’on peut mettre facilement en service. Elle adresse un nombre important de cas d’utilisation et contient les dernières évolutions de l’écosystème Hadoop. Les avantages à l’utiliser sont très nombreux. En résumé, il n’y a pas de freins à l’utiliser excepté son prix et sa forte adhérence à AWS.

Sur du long terme, les coûts de fonctionnement peuvent être ramenés au strict minimum via l’utilisation des instances ponctuelles et des clusters éphémères.

Plusieurs alternatives à EMR existent. Elles feront l’objet d’articles dédiés. Parmi ces solutions, certaines permettent de réduire encore la facture de l’infrastructure Hadoop mais ne sont pas aussi flexibles qu’EMR et amènent une gestion fine des composants.

Catégories: Blog Société

La dette technique dans un SI

La dette technique peut être définie de plusieurs façons, selon le point de vue : code, logiciel, infrastructure ou le SI en général. Dans cet article, nous allons faire un focus sur la dette technique dans le SI, ses impacts et comment la traiter.

Une définition plus détaillée avec les typologies de dette est bien expliquée dans cet article du blog octo.

La dette technique dans le SI

Dans un SI, la dette technique constitue l’écart entre l’existant et l’état de l’art des composants du SI (code, logiciel, infrastructure…)

Le remboursement de la dette d’un SI constitue l’investissement mis en oeuvre pour permettre au système d’information d’être dans l’état de l’art. Traiter la dette technique d’un SI revient à :

  • Faire Ă©voluer les composants du SI vers une cible plus rĂ©cente: traiter l’obsolescence, mise Ă  niveau des composants, etc.
  • La revue du code incompatible avec les standards et les pratiques de dĂ©veloppement (coĂ»t de non qualitĂ©)
  • Mettre Ă  niveau les Ă©quipes techniques tout le long du cycle de vie du SI
La dette est inĂ©vitable…

Fred Brooks a introduit dans son papier “No Silver Bullet” les notions de complexité essentielle et de complexité accidentelle. La première est celle liée au problème à résoudre et elle ne peut pas être évitée. La seconde n’est pas liée au problème, mais introduite par effet de bord à cause d’une analyse non approfondie du problème : sa conséquence est que le logiciel, et par conséquent le SI, devient plus compliqué à maintenir. Les deux situations engendrent une augmentation de la dette.

Qu’elle soit volontaire ou non, la dette technique augmente avec le temps.

Sur plan organisationnel, la perte de connaissance technique constitue une source de l’augmentation de la complexitĂ©. Lorsqu’on veut faire Ă©voluer un composant, des fois on peut se confronter Ă  une mĂ©connaissance des règles mĂ©tier ou du besoin originel. Sur un plan technique, un composant applicatif doit Ă©voluer, et son Ă©volution est moins rapide que les frameworks, les outils et l’infrastructure qui l’hĂ©berge, et ce, indĂ©pendamment de la qualitĂ© du code et des logiciels qui forment le système d’information.

Ce qui nous conduit au constat suivant : Même un composant applicatif de qualité génère de la dette s’il n’est pas maintenu pour être en tout temps en phase avec les standards du marché et les bonnes pratiques de développement.

Le remboursement de la dette constitue un effort, donc un budget. Si cet effort n’est pas prévu en amont, l’écart augmente et le SI deviendra de plus en plus endetté.

La dette est inévitable.

Pour donner quelques exemples concrets…

Quand on utilise un framework, celui-ci évolue avec le temps, des versions supérieures sortent. Le code en place va générer de la dette qui devra être traitée un jour. Plus le temps passe, plus la dette augmente et sera difficile à rembourser.

Quand on met en place un outil ou framework en version X, le fait que l’outil Ă©volue en version supĂ©rieure X+1, X+2,… et qu’il est, en gĂ©nĂ©ral, impossible de suivre les mises Ă  niveau au fur Ă  mesure, fait que l’on cumule de la dette qu’il faut un jour payer, Ă  travers des mises Ă  niveaux. Et si ce n’est pas fait dans les temps, le prix sera le paiement du support Ă©tendu, le recrutement de l’expertise…

Si le code n’est pas implĂ©mentĂ© en suivants les bonnes pratiques de dĂ©veloppement : couplage fort entre composants, nommage incomprĂ©hensible, code non homogène,… cela engendre un cumul de la dette au fil du temps.

Et si on parlait impacts de la dette non traitée sur le SI…

Sur l’humain : quand on cumule de la dette, on obtient un SI contenant des composants applicatifs ou infrastructure d’un autre temps. Un des impacts concret de ce type de dette est la perte de l’expertise. Les experts deviennent plus rares, et donc plus chers (exemple des experts Cobol, Mainframe…).

Sur l’environnement technique : on maintient dans le SI des infrastructures “anciennes” avec du support étendu voire obsolète des fois. On continue à utiliser des solutions ou des frameworks non supportés par des éditeurs ou la communauté (si open source). On continue à exécuter un code difficilement maintenable.

Sur le process : quand on hérite d’un SI dont les composants générent des dysfonctionnements, on rentre dans des cycles maintenance assez fastidieux, du code non testable, voire des cas extrêmes tels que l’impossibilité de déploiement de nouvelles versions.

Sur l’organisation : dans un monde qui se veut agile, avec des composants de plus en plus interconnectés, une organisation en silo est peu adaptée. Une telle organisation ne favorise pas la communication, l’échange entre équipes… et donc constitue un frein lorsqu’on veut faire évoluer le SI. Ce qui peut avoir un impact sur le traitement de la dette.

Mais quels actions pourrait-on mettre en place ? Actions correctives

Si nous sommes dans le cas où notre SI présente une dette technique à gérer, comment faire ?

Voici quelques pistes pour résorber la dette d’un SI:

  • Analyser l’ensemble des composants : selon la complexitĂ© du SI, considĂ©rer l’intĂ©rĂŞt d’utiliser un outil de type APM (Application Portfolio Management) pour assurer Ă  la fois la cohĂ©rence du SI et le suivi de la dette technique de l’ensemble des composants du SI.
  • Analyser l’impact fonctionnel (s’il existe) sur les composants Ă  modifier : la rĂ©solution de la dette peut engendrer un impact fonctionnel, certain outils ou frameworks apportent de nouvelles fonctionnalitĂ©s ou des modifications impactants le mĂ©tier.
  • Analyser l’impact sur les liens avec des composants externes.
  • Etudier le traitement de la dette en implĂ©mentant un des trois scĂ©narios : mise Ă  niveau, migration et refonte (les trois scĂ©narios sont explicitĂ©s en bas).
  • Planifier une stratĂ©gie de gestion de la dette, et commencer Ă  la traiter. Comme dit le proverbe chinois : “Le meilleur moment pour planter un arbre, c’était il y a 20 ans. Le second meilleur moment, c’est maintenant.” Il n’y a pas de temps Ă  perdre !

Revenons aux trois scénarios cités plus haut.

La mise à niveau d’un composant applicatif ou d’infrastructure consiste à le faire évoluer d’une version à une autre supérieure, avec d’éventuels impacts sur le code. Dans certains cas, la mise à niveau, surtout si elle arrive au bout de plusieurs années, peut avoir des impacts importants sur le SI, du simple fait qu’une version récente peut apporter des nouvelles fonctionnalités, voire casser des fonctionnalités existantes de la version en cours.

La migration d’un composant consiste à son remplacement par un autre, du même ou d’un autre éditeur. L’étude de la migration doit contenir une analyse détaillée des fonctionnalités utilisées, leurs équivalents dans la nouvelle solution, les éventuels impacts techniques mais aussi sur le quotidien des utilisateurs de la solution.

La refonte est essentiellement applicable sur le code d’un composant applicatif ou les composants auxquels la brique est connectĂ©e. La refonte peut ĂŞtre complète ou partielle. Dans tous les cas, il faut assurer la non-rĂ©gression, l’intĂ©gration du nouveau code et la mise en oeuvre des bonnes pratiques liĂ©es au processus de dĂ©veloppement et au code. Chez OCTO Technology, nous savons auditer les pratiques de dĂ©veloppement, qualifier un code Ă  travers certaines mĂ©triques telles quel : clartĂ©/simplicitĂ©, rĂ©utilisabilitĂ©, maintenabilitĂ©, application des principes SOLID,…

Les actions préventives

La dette étant inévitable l’objectif est d’avoir deux axes pour la traiter en amont et dans le temps.

En amont, Ă  travers:

  • La mise en place des bonnes pratiques de dĂ©veloppement dans le code et dans les processus (principes SOLID, revue de code, TDD, DDD, pair programming…)
  • L’utilisation des outils Ă©prouvĂ©s et dans l’état de l’art du marchĂ©

Dans le temps, en réservant du budget pour le traitement de la dette : Ce qui consiste à estimer les coûts de mises à niveau, migration et/ou refonte dans le temps. Comme les composants du SI évoluent et au lieu de subir cette évolution, la solution la plus adéquate est de réserver systématiquement un budget pour le traitement de la dette.

Takeaways…

La dette fait partie du SI. Elle a des impacts sur l’humain, l’environnement technique, le process et l’organisation. Le traitement de la dette doit être prévu et budgétisé au risque de voir son coût augmenter avec le temps.

Deux points importants Ă  retenir :

  • La dette est inĂ©vitable, elle fait partie du SI.
  • La dette se traite en amont mais aussi tout au long du cycle de vie d’un logiciel.

 

Articles suggested :

  1. L’architecture d’entreprise : vision mĂ©tier ou technologique?
  2. Services REST : ne jetez pas la SOA avec l’eau du bain

Catégories: Blog Société

XEBIA ACCUEILLE LE MEETUP WOMEN WHO GO PARIS

 

Lundi prochain, 11 Septembre, Xebia accueillera à partir de 19h le meetup Women Who Go, au 156 boulevard Haussmann, 7e étage.

Le but de ce meetup est d’aider les personnes qui souhaitent apprendre ou pratiquer le langage Go.

Nous allons dĂ©velopper en Go ensemble au travers d’exercices de type Kata, cela sera aussi l’occasion de tester dep, le futur standard pour la gestion de dĂ©pendances des programmes en Go .

Pour vous inscrire, rendez-vous sur le meetup Women Who Go Paris.

 

 

Catégories: Blog Société

Blockchain : Modifier un contrat immuable

Pourquoi et comment modifier un contrat immuable ? C’est ce que nous allons Ă©tudier.

Les différentes technologies de Blockchain (bitcoin, ethereum, etc.) consistent à produire un consensus, entre de nombreuses parties, sur un état stable. Pour Bitcoin, il s’agit de se mettre d’accord sur l’état d’un livre de compte ; pour Ethereum, de se mettre d’accord sur l’état d’un ordinateur virtuel mondial.

Ethereum a la prĂ©tention de permettre l’exĂ©cution de contrats numĂ©riques sous le contrĂ´le du rĂ©seau distribuĂ© et non d’une entitĂ© centralisĂ©e. Des fonds (en monnaie Ă©lectronique Ether) peuvent ĂŞtre sous la responsabilitĂ© du contrat, et le code dĂ©cide ensuite leur distribution. Typiquement, le contrat peut servir de notaire, en gardant des fonds jusqu’Ă  ce qu’une condition nĂ©cessaire soit validĂ©e (dĂ©lai, preuve de livraison, etc.).

Chaque contrat peut posséder une quantité de crypto-monnaie appelée éther. Les ethers permettent, entre autre, de financer la communauté pour l’exécution de l’ordinateur mondial.

Le principe de base d’un contrat Ethereum est d’avoir un code immuable, impossible Ă  modifier ou Ă  supprimer. Si le dĂ©veloppeur ne l’a pas prĂ©vu, il n’est pas possible d’arrĂŞter un contrat qui s’exĂ©cute dès lors qu’il est sollicitĂ©.

En effet, lorsqu’un contrat est déposé dans la chaîne de blocs, il y est pour toujours. Les instructions qui le compose peuvent être invoquées à tout moment, entraînant éventuellement une modification d’état du contrat, d’autres contrats liés, voire le transfert des Ethers associés au contrat vers un autre compte ou un autre contrat.

  • Un contrat dĂ©posĂ© dans la blockchain ne peut pas ĂŞtre supprimĂ©
  • Il peut ĂŞtre invoquĂ© par n’importe quel utilisateur ou autre contrat
  • Le code d’un contrat ne peut pas Ă©voluer
  • Seules ses donnĂ©es peuvent Ă©voluer

Modifier l’état d’un contrat, c’est écrire les différences dans le prochain bloc.

Cette immutabilité est une excellente chose, car elle permet de garantir qu’aucune des parties prenantes du contrat ne pourra revenir sur ses engagements. C’est un élément essentiel de la sécurité d’Ethereum et cela contribue à sa valeur.

Si j’accepte de signer un contrat où je m’engage à rembourser telle somme en Ether si une condition ne s’est pas produite à telle date, il m’est impossible de répudier cela. Lorsque le délai sera passé, l’autre partie pourra déclencher le versement des Ethers présents en caution dans le contrat.

Personne, mĂŞme pas un pirate, ne pourra apporter des modifications au contrat. A tel point que si le contrat est mal Ă©crit, des Ethers peuvent devenir inaccessibles.

1 Amender un contrat ?

Un contrat est immuable, mais parfois, on aimerait bien pouvoir le modifier, avec l’accord de toutes les parties. On aimerait pouvoir publier une nouvelle version du contrat.

Par exemple, il devrait être possible d’ajouter un avenant à un contrat. Ou de le modifier, car ce qui était permis ne l’est plus, des paramètres extérieurs ont évolués, de nouvelles règles sont imposées par la loi, etc.

Du point de vue des scénarios d’usage, on peut imaginer la situation suivante : deux personnes signent un contrat numérique qui est régi par la loi. La loi évolue, remettant en cause le contrat : il faut donc le modifier, avec l’accord des deux personnes.

Il est également possible d’ajouter un utilisateur représentant une puissance étatique : l’État, une instance territoriale, une institution publique, etc.

Le contrat est alors construit avec trois propriétaires : les deux personnes et, par exemple, l’État. Il est paramétré pour que deux propriétaires seulement soient nécessaires pour modifier le contrat.

Avec un scénario d’habilitation “deux sur trois”, deux partenaires parmis les trois peuvent se mettre d’accord pour modifier le contrat. Dans les faits, soit les deux personnes trouvent un nouveau terrain d’entente, soit l’une d’elles, avec l’accord de l’état, peut modifier le contrat.

Modèle d’exĂ©cution du contrat

Nous souhaitons donc pouvoir apporter toutes les modifications possibles à un contrat. Nous vous proposons ci-dessous l’approche retenue par OCTO, et permettant de minimiser les impacts sur le reste de l’écosystème :

  • Un autre contrat ou une application externe Ă  la blockchain ne doit pas ĂŞtre modifiĂ©e lors de la publication d’une nouvelle version du contrat
  • Il ne faudrait pas avoir Ă  transfĂ©rer l’état d’une version d’un contrat vers la suivante, car cela peut consommer plus d’ether que le maximum autorisĂ© par l’utilisateur du contrat et peut compromettre la sĂ©curitĂ©, en ouvrant une brèche permettant le transfert des ethers appartenants au contrat :
    • La nouvelle version doit avoir accès Ă  tous les attributs du contrat d’origine.
    • Il faut Ă©galement pouvoir transfĂ©rer les fonds de la version 1 vers la version 2. Mais le contrat version 1 ne peut avoir connaissance de la version 2.
  • Il doit ĂŞtre possible d’ajouter de nouveaux attributs et de nouvelles mĂ©thodes dans la nouvelle version du contrat : La nouvelle version du contrat ne doit pas avoir de contraintes
  • Les Ă©vĂ©nements publiĂ©s par les diffĂ©rentes versions doivent ĂŞtre Ă©mis par un seul et unique contrat
  • Les applications externes Ă  l’écoute du contrat (JavaScript ou autres) ne doivent pas ĂŞtre modifiĂ©es suite Ă  un changement de version
  • Le solde d’Ethers portĂ© par le contrat une version doit ĂŞtre disponible pour la nouvelle version

Pour cela, inspiré des travaux de Martin Swende, avec le langage Solidity, nous allons utiliser un contrat Proxy qui hérite d’un contrat Versionable. La première version du contrat devra également hériter de Versionable.

Le Proxy va exploiter la fonction fallback de Solidity, sans nom ni paramètre, qui est invoquée lorsqu’aucune méthode présente dans le contrat ne correspond. Le code de cette méthode va déléguer les data de la transaction au contrat référencé par le Proxy, mais en demandant à la machine virtuelle Ethereum (EVM) d’utiliser le contexte du contrat Proxy pour stocker les attributs (pour les détails techniques, référez-vous à cet article).

Afin de posséder l’intégralité des attributs de la première version, ContractV2 hérite de ContractV1. Cette nouvelle version peut alors ajouter de nouveaux attributs et proposer d’autres implémentations des méthodes. Il est également possible d’ajouter de nouvelles méthodes.

Avec cette approche, seul le code des versions du contrat est utilisé par le Proxy. Les données entre les versions sont mutualisées dans le Proxy. Ainsi, il n’est pas nécessaire de migrer les données du contrat V1 vers le contrat V2. Elles sont déjà présentes !

Entrons dans la technique

Pour implémenter cela, il faut écrire un peu d’assembleur EVM.

contract Versionable {
  event VersionChanged(Versionable version);

  /** The current version. */
  Versionable internal currentVersion;
  }

  contract Proxy is Versionable {

  /**
   * Create a proxy to delegate call to the current version of contract.
   * @param _currentVersion The first version to use.
   */
  function Proxy(Versionable _currentVersion) {
    currentVersion = _currentVersion;
  }

  /**
   * Change the current version.
   * @param _newVersion The new version.
   */
  function changeVersion(Versionable _newVersion) { // TODO: Add privilege
    currentVersion.kill(this);
    currentVersion = _newVersion;
    VersionChanged(_newVersion);
  }

  /**
   * Propagate the current call to another contract.
   * Use TARGET code with THIS storage, but also keep caller and callvalue.
   * Invoke delegateCall().
   * In order for this to work with callcode,
   * the data-members needs to be identical to the target contract.
   *
   * @param target The target contract.
   * @param returnSize The maximum return size of all methods invoked.
   */
  function propagateDelegateCall(address target, int returnSize) internal {
    assembly {
      let brk := mload(0x40) // Special solidity slot with top memory
      calldatacopy(brk, 0, calldatasize) // Copy data to memory at offset brk
      let retval := delegatecall(sub(gas,150)
        ,target //address
        ,brk // memory in
        ,calldatasize // input size
        ,brk // reuse mem
        ,returnSize) // arbitrary return size
      // 0 == it threw, by jumping to bad destination (00)
      jumpi(0x00, iszero(retval)) // Throw (access invalid code)
      return(brk, returnSize) // Return returnSize from memory to the caller
    }
  }

  function () payable {
    /* 32 is the maximum return size for all methods in all versions. */
    propagateDelegateCall(currentVersion,32);
  }
}

/**
 * The version 1 of the contract.
 * The attr is initialized to 1000.
 * The method doSomething() return attr + version = 1001
 */
contract ContractV1 is Versionable {
  uint constant private version=1;
  uint public attr;

  /** return attr+version (1001). */
  function doSomething() external
    returns(uint) {
   return attr+version; // 1001
  }

  /** Post-construction. */
  function init() {
    attr=1000;
    isInit=true;
  }
}

/**
 * The version 2 of the contract.
 * To preserve all the attributs from the v1 version, this version IS a ContractV1.
 * All methods can be rewrite, new one can be added and
 * some attributs can be added.
 *
 * The newAttr is initialized to 100.
 * The method doSomething() return attr + newAtttr + version = 1102
 */
contract ContractV2 is ContractV1 {
  uint constant private version=2;
  uint public newAttr;

  /** return attr + newAttr + version (1102). */
  function doSomething() external returns(uint) {
    return attr + newAttr + 2; // 1102
  }

  /** return 42. Another method in version 2. */
  function doOtherThing() external returns(uint) {
    return 42;
  }

  /** Post-construction. */
  function init() {
    newAttr = 100;
    isInit = true;
  }
}

Nous avons négligé les règles de sécurité pour simplifier le code. Il va sans dire que la méthode changeVersion() doit être protégée contre une utilisation par n’importe qui. Sinon, n’importe qui pourrait modifier un contrat.

L’intégralité des sources est disponible ici.

Le proxy ne possède pas de méthode, mais va gérer les attributs du contrat. Les méthodes sont présentes dans les versions du contrat. Si on inspecte l’instance ContractV1, aucun attribut n’est présent. De même pour ContractV2.

Le deuxième effet kisscool de ce modèle est que les events viennent du Proxy et non des contrats. Donc tant que le format des événements n’est pas modifié, les applications à l’écoute du contrat n’ont pas à savoir qu’il a été modifié. Elles écoutent toujours les mêmes événements, venant du même contrat.

Pour le développeur, il teste et conçoit les contrats comme d’habitude. C’est lors du déploiement que la localisation des attributs change.

Il reste à gérer les constructeurs des contrats. En effet, construire une instance est un traitement spécial. Il est envoyé au contrat de numéro zéro de la blockchain. Le code du constructeur n’est pas disponible avec le contrat. Il n’est donc pas possible de le réutiliser pour initialiser le proxy.

Nous devons alors utiliser une méthode init() qui jouera le rôle de constructeur. Il ne faut pas oublier de l’invoquer juste après la création de l’instance du contrat v1 et du contrat v2.

Comment utiliser ce code ? Il suffit de construire une instance du ContractV1, de l’encapsuler dans un Proxy et de caster (transtyper) le proxy en ContractV1.

ContractV1 myContract = ContractV1(new Proxy(new ContractV1()));

Ensuite, il ne faut pas oublier d’invoquer l’initialisation du contrat, en passant bien par le proxy.

myContract.init();

Pour utiliser le contrat, c’est comme d’habitude.

myContract.doSomething();

Et voilà. Pour modifier la version, on recast le contrat en Proxy, afin d’avoir accès à la méthode changeVersion().

Proxy(myContract).changeVersion(new ContractV2());

L’invocation de la nouvelle version est identique et s’effectue toujours avec myContract.

myContract.doSomething();

Ce dernier modèle répond à toutes les exigences :

  • La rĂ©fĂ©rence du contrat n’évolue pas, mĂŞme en cas de changement d’implĂ©mentation
  • Il est possible d’ajouter de nouvelles mĂ©thodes ou de nouveaux attributs dans une nouvelle version du contrat
  • Il n’est pas nĂ©cessaire de migrer les donnĂ©es entre les versions
  • Les Ă©vĂ©nements Ă©mis par le code des contrats V1 ou V2 viennent bien du proxy

Les inconvénients de cette approche sont les suivants :

  • Il n’est pas possible de supprimer un attribut dans la dĂ©finition de la classe
  • Il est nĂ©cessaire de sĂ©parer le constructeur de la mĂ©thode init()
  • Cela prĂ©sente un surcoĂ»t de 735 gaz pour chaque invocation (soit 0,00000252 € avec un Ether Ă  120 €).
  • Il faut connaĂ®tre la taille maximum acceptable des returns (cf. paramètre returnSize)

Pour résoudre ce dernier point, une proposition d’évolution de l’EVM est en discussion (EIP-5).

2 Et la sécurité ?

Pour autoriser la modification de la version du contrat, il faut, par exemple, que n owners parmi m soient d’accord sur la nouvelle implémentation du contrat (n pouvant être égal à m). Lorsqu’une méthode sensible est invoquée, elle n’est pas exécutée directement tant qu’un autre owner n’invoque pas la même méthode avec strictement les mêmes paramètres. Lorsque suffisamment d’owners sont d’accords pour modifier le contrat, alors la version est modifiée.

OCTO propose les contrats MultiOwned, Versionable et Proxy que vous pouvez utiliser pour tous vos nouveaux contrats.

Vous retrouverez ici la version protégée du Proxy, permettant à plusieurs owners de se mettre d’accord sur la nouvelle version du contrat. La liste des owners est à spécifier lors de la création du Proxy et peut ensuite être modifiée avec l’accord de tous.

Tous les sources sont disponibles ici.

Nous proposons un exemple d’utilisation de ce modèle avec les sources. Pour le tester, il faut :

  • CrĂ©er une instance du test unitaire
  • Invoquer init()
  • Invoquer doSomething() pour rĂ©cupĂ©rer 1001 (version 1 du traitement)
  • Demander le changement de version via l’utilisateur 1 ( user1_changeToV2() )
  • Confirmer la demande de changement en invoquant de mĂŞme changeVersion() avec strictement les mĂŞmes paramètres, mais via l’utilisateur 2 ( user2_changeToV2() )
  • Invoquer doSomething() pour rĂ©cupĂ©rer 1102 (version 2 du traitement)
  • et enfin doOtherthing() pour confirmer qu’il est possible d’ajouter une nouvelle mĂ©thode
3 Dans le détail du code assembleur

Cette implémentation se base sur quelques subtilités de l’Ethereum Virtual Machine et de Solidity. Si vous souhaitez plus de détails techniques, nous vous invitons à regarder cet autre article de blog.

L’invocation d’une méthode est intégralement décrite dans le paramètre data d’une transaction. Il est donc possible de proposer la même invocation à un autre contrat. Pour cela, nous devons utiliser l’instruction delegatecall. Cette dernière nécessite d’indiquer une zone en mémoire avec les paramètres de l’invocation, et une autre zone en mémoire pour récupérer le résultat de l’invocation. Pour utiliser une zone mémoire disponible, nous commençons par récupérer la valeur à l’index 0x40, utilisé par Solidity. Elle indique la dernière adresse mémoire utilisée par le programme. Nous utilisons alors cette zone vierge pour y répliquer les données de msg.data. Nous indiquons la même zone mémoire pour récupérer le résultat de l’invocation. En cas d’erreur, nous lançons un jump vers une zone de code invalide. Cela est l’équivalent à un throw sous Solidity. Enfin, nous retournons la zone mémoire valorisée par l’invocation du delegateCall.

Nous plaçons cette méthode spéciale dans la méthode de repli de Solidity, afin que toutes les méthodes absentes du contrat Proxy soient déléguées à l’instance portant la version du contrat. Toutes les modifications des attributs s’effectuent sur l’instance Proxy.

Nous vous proposons une solution générique. Elle utilise les spécificités de la machine virtuelle et des choix d’implémentations de Solidity :

  • Utiliser le fait qu’un Cast est possible vers n’importe quelle adresse de contrat. Cela permet de faire passer le Proxy comme un ContractV1 ou ContractV2
  • Utiliser la mĂ©thode par dĂ©faut, lorsqu’une mĂ©thode n’est pas identifiĂ©e par le contrat
  • Utiliser l’attribut Ă  l’adresse 0x40 pour identifier une zone mĂ©moire disponible pour dĂ©lĂ©guer le traitement
  • Utiliser l’assembleur pour invoquer une mĂ©thode d’un autre contrat, et rĂ©cupĂ©rer la valeur de retour avant de la propager Ă  l’appelant
  • Utiliser la dĂ©lĂ©gation pour que les Ă©vĂ©nements des diffĂ©rentes implĂ©mentations viennent bien du Proxy

OCTO propose une solution générique, de quelques lignes, permettant de limiter au maximum les impacts de la mise à jour d’un contrat.

Articles suggested :

  1. Introduction Ă  la technologie Blockchain
  2. Une cagnotte dans la blockchain Ethereum
  3. Raiden, une réponse à la confidentialité des échanges sur Ethereum ?

Catégories: Blog Société

Le demi-cercle (épisode 2 — Voir / Avancer)

(RĂ©sumĂ© des Ă©pisodes prĂ©cĂ©dents : Et si c’Ă©tait le moment oĂą on pose son ouvrage et on rĂ©flĂ©chit ? Si c’Ă©tait le moment oĂą l’on commence Ă  changer un peu la manière dont on fait les choses ?)

Prenons ce bug. Tu viens d’identifier l’origine du problème, et aussitĂ´t tu t’es figurĂ© la solution. En un clin d’Ĺ“il. Tu ouvres le code, tu fais la modification qui va bien. Tu relances l’assemblage. Dans dix minutes, l’application sera livrable en recette. Il faudra remettre en place des donnĂ©es de test pour le compte du client. Il faudra attendre que le client fasse ses tests, et donne son feu vert pour le passage en production.

Tu réfléchis. Tu fais les questions et les réponses.
– Et si ça ne devait pas marcher ?
– Ça marche; j’en suis sĂ»r.
– Et si ça ne devait pas marcher ?
– Ça ne se serait pas de notre fait.
– Si ça ne devait pas marcher, combien de temps faudrait-il pour en ĂŞtre informĂ© ?
– Plusieurs heures, voire plusieurs jours.

Tu réfléchis encore.
– De deux choses l’une : soit le programme est maintenant corrigĂ©, soit il comporte (au moins) un nouveau dĂ©faut.
– S’il est corrigĂ©, alors nous faisons toutes ces dĂ©marches de test pour rien.
– S’il ne l’est pas, alors il vaut mieux attendre d’en ĂŞtre certain avant de rĂ©agir.

Tu marches Ă  tâtons dans l’obscuritĂ©. L’incertitude. Ce qui est certain, c’est que la pièce est assez vaste — on dirait qu’elle rĂ©sonne. Et aussi qu’elle est particulièrement encombrĂ©e, par un tas de trucs indĂ©finis, non identifiĂ©s, un fatras. Mais tu ne peux rien voir. Tu peux seulement tenter d’avancer, et buter dans l’obstacle qui se trouve lĂ . Ce jeu — que tu adorais Ă  quinze ans mais qui commence Ă  te fatiguer maintenant que tu en as presque trente — possède une règle bien identifiĂ©e par tous les joueurs :

« voir » = buter dans un obstacle.

Dans cette situation, quelle sera ton approche prĂ©fĂ©rĂ©e ? Est-ce que tu ralentis et tends les mains pour dĂ©tecter les obstacles, ou bien est-ce que tu t’engages prestement et traverse la pièce avec aplomb ? D’autres se mettraient tout de suite Ă  quatre pattes, pour ne pas risquer une chute. Petits joueurs. Marcher Ă  quatre pattes, c’est s’avouer vaincu d’avance.

Tu marches Ă  l’aveugle, mais debout. Tu prĂ©tends pouvoir sortir de la pièce en un seul trajet. Tu affirmes : « C’Ă©tait le dernier dĂ©faut ! » Tu dis : « Il reste juste ce tout petit problème Ă  corriger et on est bons. » Mais pas une seule de toutes les parties prĂ©cĂ©dentes que tu as jouĂ©es ne s’est dĂ©roulĂ©e sans que tu ne rencontres, en le percutant, un obstacle. Et Ă  chaque fois, tu t’es cassĂ© la figure, tu as ramassĂ© et rĂ©arrangĂ© Ă  la hâte les trucs en questions, tu t’es figurĂ© que ça devait vaguement former un bloc, une pile, ou bien au moins un tas, et tu t’es relevĂ©. VoilĂ . Ça devrait faire le job. Tu aimerais bien rentrer chez toi maintenant. Tu te dis : « demain il fera jour ». Mais demain quand tu reviendras dans la pièce, elle sera tout aussi obscure. Et Ă  nouveau, peut-ĂŞtre, tu buteras dans des nouveaux obstacles, de forme et de taille inconnues.

Bombes logiques. À mesure que tu corriges des défauts, tu en sèmes de nouveaux.

Ce que tu pourrais faire de différent ?

Tu ne peux certes pas allumer dans la pièce.
Si tu pouvais allumer, cela résoudrait une bonne partie du problème, évidemment.

Tu rĂŞves qu’on allume dans la pièce, et dans ta tĂŞte tu entends comme une foule soudain satisfaite qui soupire : « Aaaaah! », et tu regardes :

Le premier obstacle dans lequel tu butais systĂ©matiquement n’Ă©tait pas bien rangĂ©. Une fois placĂ© debout près de ce pilier, cet obstacle ne gĂŞnerait plus du tout, en fait.

Tu vois clairement maintenant certains obstacles que tu croyais insurmontables, inĂ©luctables : tu rĂ©alises qu’on peut les faire disparaĂ®tre en deux-temps, trois mouvements.

Cet obstacle dont tu te disais : « ça doit ĂŞtre le dernier », se trouve comme Ă©talĂ© sur toute la surface de la pièce, tu peux Ă  peine compter les occurrences, il n’y en a pas « juste un dernier », il en reste des douzaines.

Et dans cette lumière éclatante et cette netteté nouvelle, tu es sur le point de réaliser une deuxième règle que beaucoup moins de joueurs connaissent :

« avancer » = créer de nouveaux obstacles

Mais tu ne peux pas vraiment formuler cette règle numĂ©ro 2 parce que tu n’as ni les moyens (il faudrait savoir comment) ni le temps (il faudrait des mois) de rĂ©tablir la lumière dans ce projet aveugle.

TrĂŞve de rĂŞveries.

Que faire ?
Une pause.

Tu vas au coin cuisine/café/baby et tu te prépares une soupe Ramen.
Tu reviens et tu la manges devant ton Ă©cran, qui dĂ©place en silence des couleurs fauves autour d’une horloge qui dit : 20h45.
Avec un petit jus de mangue, et un cookie Chinois.
Tu ouvres le cookie Chinois, tu déplies le papier.

On apprend tous de nos erreurs… Vous allez beaucoup apprendre aujourd’hui.

Très drĂ´le. Tu repenses Ă  la blague de JĂ©rĂ©mie hier midi. JĂ©rĂ©mie — qui fait montre d’un esprit moqueur et d’un vif scepticisme Ă  l’Ă©gard de tout ce qui est mĂ©thodologie — ouvre un cookie Chinois et lit d’un ton docte : « Chaque ligne de code est justifiĂ©e par un test qui ne passait pas ». Ce qui a fait rire toute la tablĂ©e, mĂŞme ceux qui entendaient ce truc pour la première fois.

Cette blague. Chaque ligne de code est justifiĂ©e par un test qui ne passait pas. Et puis quoi d’autre ?

Tu déverrouilles ton écran. Pourquoi pas ? Tu peux décider maintenant, comme une sorte de principe de survie en milieu obscur, que les obstacles rencontrés ne se remettront pas dans ton chemin.

Tu reviens en arrière; tu reprends la version bugguĂ©e du programme; tu crĂ©es un module de test; tu Ă©cris un test qui ne passera pas; il ne passera pas parce qu’il y a cette erreur — ce bug. Écrire le test te prend du temps. Le code ne s’y prĂŞte pas, ne se soumet pas facilement Ă  des tests de cette sorte. C’est parce que le test vise une partie du code qu’il faudrait exĂ©cuter indĂ©pendamment de tout le reste. C’est d’autant plus idiot que cette partie est toute petite. Deux lignes.

Le code est couplĂ© Ă  ses dĂ©pendances extĂ©rieures. Tu mets l’effort que requiert la crĂ©ation de ce test en regard de l’insignifiance de la ligne de code qu’il permet de tester. Tout ça pour ça. La montagne accouche d’une souris. Frustration.

Tu es arrĂŞtĂ© dans tes pensĂ©es par le vrombissement du tĂ©lĂ©phone. C’est StĂ©phanie.
– J’ai eu ton message. J’ai dĂ®nĂ©. Tu en as pour longtemps ?
– Plus tellement. Je suis crevĂ©. Ça va ?
– J’essaie de rĂ©parer mon vĂ©lo. C’est le dĂ©railleur.
– Oh!
– C’est pĂ©nible. J’ai essayĂ© de le rĂ©gler mais je ne peux pas manĹ“uvrer le dĂ©railleur sans pĂ©daler et rouler. Et quand je roule, je ne peux pas regarder le dĂ©railleur de près. En plus il fait nuit, et la lumière de la cour est trop faible.
– Ah.
– Donc je retourne le vĂ©lo, je le mets sur sa selle et j’actionne les pĂ©dales. Mais dans ce cas lĂ , la chaĂ®ne ne s’enroule pas de la mĂŞme façon sur le dĂ©railleur parce que le vĂ©lo est dans l’autre sens.
– …
– Et toi comment ça va ?
– Pareil.
– Tu rentres Ă  quelle heure ?
– Je pars dans 10 minutes.

Tu te dis : « Ă€ quoi bon, c’est impossible ». Mais rĂ©flĂ©chis. Le dĂ©lai qu’il te faudrait pour dĂ©coupler un peu ce code et poser ce test, tu vas le passer Ă  attendre que le client fasse ses tests ?

Tout en insĂ©rant d’autres bombes logiques dans le programme ?

Tu reprends l’idĂ©e. Tu casses les dĂ©pendances. C’est assez moche. Le code semble encore un peu plus baroque qu’il ne l’Ă©tait avant ce test. Tu arrives tant bien que mal Ă  isoler le code incriminĂ©. Ce n’est pas spectaculaire, mais au moins ton test s’exĂ©cute sans faire appel Ă  la base de donnĂ©es.

Il est rouge.
Tu corriges le bug.
Le test passe au vert.

Tu as peut-ĂŞtre encore le temps d’Ă©crire un autre test, du coup. Cette partie du code est totalement (dans l’obscuritĂ©) dĂ©pourvue de tests.
Pour cette partie du code, tu ne sais mĂŞme pas ce qu’il faudrait que le test affirme. Tu exĂ©cutes le test, et le rĂ©sultat te dit ce que fait effectivement le code. Tu colles ce rĂ©sultat dans le test, tout en n’Ă©tant pas sĂ»r que ce soit la vĂ©ritĂ©. Tu te dis qu’en tout Ă©tat de cause, tu as dĂ©terminĂ© ce que fait le code, et que tu pourras toujours t’appuyer sur ce test pour poser des questions Ă  la responsable d’application. Ce sera toujours plus fiable que de lancer des hypothèses dans le noir et se casser la figure ensuite.

Tu dĂ©cides pour un temps de garder ce principe, d’Ă©crire un test pour chaque nouvel Ă©lĂ©ment de fonctionnalitĂ© que tu voudras crĂ©er, ou modifier.

C’est comme si Ă  chaque fois que tu dois explorer la pièce, tu pouvais poser Ă  l’endroit oĂą tu te trouves, une lumière, certes pas très puissante, mais qui, tout le temps que ce projet durera, ne s’Ă©teindra plus.

Tu verrouilles ton poste, et tu rentres.

Catégories: Blog Société

Design stratégique et jeu de Go

L'actualité de Synbioz - jeu, 09/07/2017 - 23:00

Dans l’article précédent nous avons effleuré le concept de design stratégique. La phase indispensable qui consiste à analyser notre système sous l’aspect et dans le référentiel du problème (par opposition à la solution).

Lire la suite...

Catégories: Blog Société

Revue de Presse Xebia

revue de presse Xebia
La revue de presse hebdomadaire des technologies Big Data, DevOps et Web, architectures Java et mobilité dans des environnements agiles, proposée par Xebia.

Agilité Les neurosciences et le management http://blog.xebia.fr/author/nullhttp://twitter.com/https%3A%2F%2Ftwitter.com%2Fgael_rebmannhttps://thefnublog.com/Par Gaël Rebmann

Cet article de HBR nous fournit une introduction sur l’utilisation des neurosciences dans notre management. Plus que d’utiliser des outils et des règles préétablies (telles que les moving motivators de Jurgen Appelo), les neurosciences nous promettent la possibilité de comprendre le fonctionnement du cerveau et ainsi de mieux cerner les besoins de nos collaborateurs. La maîtrise de cette science permet alors d’améliorer ces outils et ces règles de management. C’est en tout cas ce que nous promet cet article !

Mobilité Comparaison entre ARKit et ARCore http://www.gravatar.com/avatar/e91bf6e478f34fa8c8378386d332ae44http://blog.xebia.fr/author/nullhttp://github.com/mbretonPar Jose Antonio Martin Sanchez

Suite Ă  la prĂ©sentation de ARKit par Apple, Google n’a pas trainĂ© les pieds et il vient de prĂ©senter sa toute nouvelle plateforme de rĂ©alitĂ© augmentĂ©e baptisĂ©e ARCore. BasĂ©e sur la plateforme Tango, elle essaie de contrer l’enthousiasme provoquĂ© par le nouveau framework de la marque Ă  la pomme. Dans cet article, l’auteur Matt Miesnieks dĂ©cortique les points forts et les points faibles de chaque technologie.

ARCore, la réponse de Google face à Apple et son ARKit http://blog.xebia.fr/author/nullhttp://github.com/mbretonPar Souhaib Guitouni

Google va désormais se concentrer sur ARCore et abandonner progressivement le projet Tango.

Avec une flotte de plus de 100 millions d’appareils compatible à la fin de sa phase de bêta, Google vise à démocratiser l’accès à la réalitée augmentée sous sa plateforme Android. ARCore détermine la position et l’orientation du téléphone pendant qu’on le manipule. Les objets virtuels restent ainsi en place il peut aussi détecter les surfaces horizontales, comme les tables ou les sols, où placer les objets virtuels. Il permet même de mesurer la lumière ambiante afin de permettre aux développeurs d’éclairer les objets virtuels d’une manière réaliste

ARCore mesure la lumière ambiante afin de permettre aux développeurs d’éclairer les objets virtuels d’une manière réaliste.

Les applications comme SnapChat vont-elles s’emparer de ces nouveaux supports pour apporter encore plus de réalisme aux utilisateurs ?

BientĂ´t des snaps avec vos dragons domestiques de GOT !

IoT Module LoRaWAN avec ConnectivitĂ© SigFox http://blog.xebia.fr/author/nullhttp://github.com/mbretonPar Yassir Sennoun Murata, un constructeur japonais d’électronique, propose un nouveau module radio qui embarque Ă  la fois une puce LoRaWAN et Sigfox. Avec ce module, il est alors possible de dĂ©velopper un objet connectĂ© communicant avec la technologie LoRaWAN et Sigfox. http://www.vipress.net/module-lorawan-connectivite-sigfox-murata/ DevOps Mise en place d’un SSL en toute simplicitĂ© http://www.gravatar.com/avatar/37a6259cc0c1dae299a7866489dff0bdhttp://blog.xebia.fr/author/nullhttp://twitter.com/nullhttp://github.com/sarahBuissonPar Sarah Buisson

 Voici un tutorial  pour mettre en place rapidement, facilement et gratuitement un certificat SSL/TLS (Secure Socket Layer – vous savez, les url en https avec un petits cadenas ) sur vos serveurs publics, le tout grâce Ă  SSL for Free.

Catégories: Blog Société

Introducing Elements: The JIRA app to manage lists, not sub-tasks

Le blog de Valiantys - jeu, 09/07/2017 - 10:45

Even as project management technology advances forward, there are still underlying truths to how people work. In the words of H. Allen Smith, “The human animal differs from the lesser primates in his passion for lists.” Obviously this satire statement is a bit dramatic, but if you are a JIRA admin you’re probably nodding your head ...

The post Introducing Elements: The JIRA app to manage lists, not sub-tasks appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Partagez la connaissance

Partagez BlogsdeDeveloppeurs.com sur les réseaux sociaux