Giter VIP home page Giter VIP logo

iot-development-challenge's Introduction

IoT Development Challenge

Ce repository contient l'ensemble des éléments pour le concours de développement IoT :

  • Le fichier Swagger décrivant l'interface REST à développer.
  • Les scripts Gatling d'injection pour les performance (en cours).

Rendez-vous sur http://editor.swagger.io/#/ pour générer le code serveur qui absorbera les 100 000 messages.

Raspberry

FAQ

Quel niveau de persistance est attendu ? Doit-on, à minima, persister l’ensemble des données reçues sur la carte SD ?

Oui. L'ensemble des données reçues doivent être stockées sur la carte SD.

Doit-on également persister la synthèse ?

Pas forcement. Cependant un reboot doit permettre de la calculer.

Les requêtes doivent être traitées de manière synchrone : Quand l’appli dit « 200 OK », les données de la requête sont traitées et incluses dans la synthèse si elle est demandée. L’ordre des traitements entre les requêtes est-il important ? En particulier entre deux requêtes d’un même capteur ?

Non l'ordre n'a pas d’importance. Cependant l’identifiant du message doit être vérifié. Chaque message reçu doit être unique. Le service de synthèse doit être en mesure de restituer la synthèse des messages reçus lors de l'appel au service.

Quel est le livrable ? Une carte SD avec tout dessus

Une carte de bonne facture sera acquise sur chaque site. Vous pouvez venir avec votre carte micro-sd ou venir avec un dump de la carte. Les tests finaux seront réalisés avec la même carte pour plus d'équité.

L’appli doit-elle booter avec le raspberry ?

Oui, complètement. Le serveur doit écouter sur le port 80 et le chemin des services doit être celui du fichier Swagger. Le raspberry doit avoir comme IP : 192.168.1.1 / 255.255.255.0

Le choix de l’OS est-il libre ?

Oui, à vous de choisir ! Soyez audacieux !

Les temps de tirs de test des équipes seront-ils échangés entre les équipes pour se challenger ?

Oui, on va essayer de mettre une page Web pour partager les résultats au fur et à mesure de l'avancement des équipes. Le script Gatling postera les résultats quand des tirs seront effectués.

Y-a-t'il une tolérance sur le choix de la carte SD pour la livraison ?

Nous utiliserons des cartes de bonne capacité en lecture / écriture. La finale sera réalisée sur la même carte pour plus d’équité. Les tests intermédiaires peuvent être réalisés sur vos propres cartes si vous le souhaitez.

Quel est le format de la date du timestamp du message ?

Le timestamp est un DateTime RFC3339 : 1985-04-12T23:20:50.52Z

Quelle est la taille max de l'identifiant du message ?

64 caractères.

Peut avoir quelques infos sur le scénario du test ainsi que sur le nombre d'utilisateurs max en parallèle?

Le scénario de test enchainera écritures et lectures de synthèses. 10 injecteurs seront lancés, chaque injecteur écrira 10 000 messages.

Doit-on vérifier l'unicité de l'identifiant du message et renvoyer une erreur dans le cas d'un POST double ? Est-ce obligatoire ou pas ?

Bonne remarque, c’est effectivement pertinent de rendre obligatoire la vérification de l’unicité de l’identifiant. Un second message portant le même identifiant qu’un identifiant préalablement reçu ne doit pas être pris en compte par la gateway.

Est-ce que /messages/synthesis doit renvoyer un tableau avec une entrée "synthesis" par type de capteur ?

Oui, une entrée "synthesis" doit être retournée par type de capteur.

Est-ce le timestamp des messages qui doit être utilisé ou la date de prise en compte dans le serveur ?

Le Timestamp du message fait foi. La date de prise en compte de la demande de synthèse - 60 minutes.

Combien de types de capteurs sont à gérer ?

int32 ;-)

Quel est le niveau de précision à gérer sur la moyenne dans le service de synthèse ?

Deux décimales après le '.' seront suffisantes.

iot-development-challenge's People

Contributors

dpiraud avatar isbena avatar ltoinel avatar slyfer-azuma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iot-development-challenge's Issues

Gatling traitement JSON

Le traitement du JSON n'est pas bon, la variable b devrait être remplacée par sensorTypeIndex

Gatling baseURL

On devrait avoir
val httpProtocol = http .baseURL("http://") .inferHtmlResources()
et non
val httpProtocol = http .baseURL("http://gatling.io") .inferHtmlResources()

java.text.SimpleDateFormat n'est pas thread safe

Le script gatling utilise une seule instance de java.text.SimpleDateFormat pour tous les threads.
Pendant l’exécution, en particulier pour la partie calcul des dates pour les demandes de synthèses intermédiaires, j'ai des effets de bord.
Sur mon poste, j'ai corrigé en créant une instance à chaque utilisation.

Doc:
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

==>Confirmez-vous mon analyse? Si oui, je ne sais pas ce qui sera le plus propre: Instance par thread, nouvelle instance à chaque utilisation, bloque synchronisé...

Durée de la synthése demandée: s, ms ou pause plus longue?

Dans le script gatling, après avoir envoyé un paquet de N rqt, il y a une instruction pause(1) qui permet de terminer la seconde courante pour pouvoir, ensuite, envoyé la durée de la synthèse demandée en secondes en "coupant" les ms.
Ce fonctionnement semble KO: Je suppose que pause() est basé sur Thread.sleep() et Thread.sleep() ne garantit pas que la durée de la pause soit d'au moins le temps demandé:
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#sleep(long)

Avec le code tout en bas qui log le timestamp avant/après la pause, la pause est mesurée entre 489 et 1262 ms pour le script.... Donc on peut perdre 500ms de données avec pas de chance.

Pour moi, on peut
_Augmenter "à la louche" la pause pour être assuré d'avoir au moins 1 seconde de pause.
_Envoyer la durée de la simulation demandé directement en ms (Pour les synthèses intermédiaires et pour la synthèse globale).
==>Je penche pour la seconde proposition :)

... 
            .repeat(msgPackage){  

                exec(http(requestName(sensorIndex))
                    .post(url)
                    .body(StringBody(session=>generateJson(sensorIndex))).asJSON
                    .headers(header)
                    .check(status.is(200))
                    )   

            }
      .exec(
           session=>{
           println(" "+ sensorIndex + " ===> " + Calendar.getInstance().getTimeInMillis()) 
           session
           }
      )
      .pause(1)

            //get the parameters of the synthesis call
            .exec(session=>{
      println(" " + sensorIndex + " <=== " + Calendar.getInstance().getTimeInMillis())
...

Le script gatling cherche une attribut absent du swagger

Bonsoir,
Pendant mes tentatives de ce soir et sauf erreur de ma part, le script gatling semble chercher un attribut de la synthèse qui n'est pas défini dans le swagger (SynthesisSensor+index du capteur), la stack:

java.lang.UnsupportedOperationException: Can't call get on Failure(No attribute named 'SynthesisSensor5' is defined)
at io.gatling.commons.validation.Failure.get(Validation.scala:50)
at io.gatling.commons.validation.Failure.get(Validation.scala:42)
at InjectionsAndVerifications$ScenarioBuilder$$anonfun$SendMsgsAndCHeckSynthesis$2.apply(script.scala:298)
at InjectionsAndVerifications$ScenarioBuilder$$anonfun$SendMsgsAndCHeckSynthesis$2.apply(script.scala:296)
at io.gatling.core.action.SessionHook.execute(SessionHook.scala:38)
at io.gatling.core.action.Action$class.$bang(Action.scala:35)
at io.gatling.core.action.SessionHook.io$gatling$core$action$ChainableAction$$super$$bang(SessionHook.scala:30)
at io.gatling.core.action.ChainableAction$class.$bang(Action.scala:60)
at io.gatling.core.action.builder.SessionHookBuilder$$anon$1.io$gatling$core$action$ExitableAction$$super$$bang(SessionHookBuilder.scala:35)
at io.gatling.core.action.ExitableAction$class.io$gatling$core$action$ExitableAction$class$$$anonfun$3(BlockExit.scala:140)
at io.gatling.core.action.ExitableAction$class.io$gatling$core$action$ExitableAction$class$$$anonfun$3$adapted(BlockExit.scala:140)
at io.gatling.core.action.ExitableAction$.exitOrElse(BlockExit.scala:125)
at io.gatling.core.action.ExitableAction$class.$bang(BlockExit.scala:140)
at io.gatling.core.action.builder.SessionHookBuilder$$anon$1.$bang(SessionHookBuilder.scala:35)
at io.gatling.http.ahc.ResponseProcessor.executeNext(ResponseProcessor.scala:144)
at io.gatling.http.ahc.ResponseProcessor.logAndExecuteNext(ResponseProcessor.scala:169)
at io.gatling.http.ahc.ResponseProcessor.checkAndProceed$1(ResponseProcessor.scala:291)
at io.gatling.http.ahc.ResponseProcessor.processResponse(ResponseProcessor.scala:321)
at io.gatling.http.ahc.ResponseProcessor.onCompleted(ResponseProcessor.scala:61)
at io.gatling.http.ahc.AsyncHandler.onCompleted(AsyncHandler.scala:127)
at io.gatling.http.ahc.AsyncHandler.onCompleted(AsyncHandler.scala:46)
at org.asynchttpclient.netty.NettyResponseFuture.getContent(NettyResponseFuture.java:188)
at org.asynchttpclient.netty.NettyResponseFuture.done(NettyResponseFuture.java:223)
at org.asynchttpclient.netty.handler.HttpHandler.finishUpdate(HttpHandler.java:58)
at org.asynchttpclient.netty.handler.HttpHandler.handleChunk(HttpHandler.java:159)
at org.asynchttpclient.netty.handler.HttpHandler.handleRead(HttpHandler.java:189)
at org.asynchttpclient.netty.handler.AsyncHttpClientHandler.channelRead(AsyncHttpClientHandler.java:82)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:428)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:277)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:264)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:243)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:962)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:485)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:399)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:371)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Unknown Source)

Mauvaise comparaison des résultats de synthèse

Je crois que cette comparaison sera tous le temps à false:
session(SynthesisSensorNum(sensorIndex)).validate[Vector[Any]].get(getSynthesisResultIndex(sensorIndex,"maxValue"))==partialMaxValue(sensorIndex)
Tous comme la comparaison du minValue et du mediumValue.

puisqu'on compare un string à un int à chaque fois.

Erreur de compilation

Impossible de lancer le script.

Voici les logs:
GATLING_HOME is set to /Users/anouar/Documents/gatling-charts-highcharts-bundle-2.2.0
20:44:39.563 [WARN ] i.g.c.ZincCompiler$ - Pruning sources from previous analysis, due to incompatible CompileSetup.
20:44:52.833 [WARN ] i.g.c.ZincCompiler$ - /Users/anouar/Documents/gatling-charts-highcharts-bundle-2.2.0/user-files/simulations/gatling_script.scala:381: non-variable type argument Map[String,Any] in type pattern List[Map[String,Any]](the underlying of List[Map[String,Any]]) is unchecked since it is eliminated by erasure
20:44:52.834 [WARN ] i.g.c.ZincCompiler$ - case Some(m: List[Map[String, Any]]) => (m(sensorTypeIndex))(synthesisValue) match{
20:44:52.836 [WARN ] i.g.c.ZincCompiler$ - ^
20:44:53.104 [ERROR] i.g.c.ZincCompiler$ - /Users/anouar/Documents/gatling-charts-highcharts-bundle-2.2.0/user-files/simulations/gatling_script.scala:399: value unary_+ is not a member of String
20:44:53.105 [ERROR] i.g.c.ZincCompiler$ - +java.net.URLEncoder.encode(formatter.format(simulationStartTime), "utf-8")
20:44:53.105 [ERROR] i.g.c.ZincCompiler$ - ^
20:44:53.108 [ERROR] i.g.c.ZincCompiler$ - /Users/anouar/Documents/gatling-charts-highcharts-bundle-2.2.0/user-files/simulations/gatling_script.scala:400: value unary_+ is not a member of String
20:44:53.108 [ERROR] i.g.c.ZincCompiler$ - +"&duration="+((timeOfSimulation/1000000000)+1).toInt
20:44:53.109 [ERROR] i.g.c.ZincCompiler$ - ^
20:44:53.119 [WARN ] i.g.c.ZincCompiler$ - one warning found
20:44:53.120 [ERROR] i.g.c.ZincCompiler$ - two errors found
20:44:53.125 [ERROR] i.g.c.ZincCompiler$ - Compilation crashed

Est ce qu'il faut utiliser une version spécifique? ou bien c'est vraiment le script qui ne compile pas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.