Skip to content

Instantly share code, notes, and snippets.

@chussenot
Created October 4, 2016 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chussenot/1c1d1fcf92296f05beef423367222d77 to your computer and use it in GitHub Desktop.
Save chussenot/1c1d1fcf92296f05beef423367222d77 to your computer and use it in GitHub Desktop.

Matcher

Dans le cadre de l'optimisation des temps de recherche des matchs, ci-dessous un ensemble de points notable qui peuvent avoir une influence sur la performance globale.

Logique

Workers

L'application wayzup-matcher est constitué en partie d'un ensemble de workers avec une converture de test faible.

  • account_created_worker.rb
  • account_deleted_worker.rb
  • account_updated_worker.rb
  • administrator_created_worker.rb
  • administrator_deleted_worker.rb
  • administrator_updated_worker.rb
  • favorite_ride_regenerate_matchings_worker.rb
  • favorite_trip_upsert_worker.rb
  • regenerate_offers_for_mate_worker.rb
  • send_ttin_worker.rb

Il existe deux types de workers, le premier groupe est en charge de syncroniser les ressources Mate et Administrator qui sont présentes dans deux applicatifs wayzup-apiet wayzup-matcher.

Le déclenchement de la mise à jour d'une ressource doit si j'ai bien compris n'être démarré que par les événements suivants create, update, delete depuis l'application wayzup-api.

Mate

La suppression d'un Mate dans le job account_deleted_worker.rb supprime aussi un ensemble de ressource, Le FavoriteTrip, les FavoriteRide, tous les FavoriteRidesMatching dans lequel le Mateest passager ou conducteur.

La MAJ d'un Mateva déclencher le recalcul des offres Offer si son commute_mode (moyen de déplacement) change. Une désactivation (status = :inactive) des FavoriteRidesMatching concernant ce Mateet lance le déclenchement d'un job MateOffersUpdatedWorker une fois les opérations finies.

Administrator

Cette ressource ne permet que l'accès à un segment de l'API /admin_v1 du Matcher. Les workers n'ont pas d'impact sur les autres entités du système.

FavoriteTrip

Le job favorite_trip_upsert_worker.rb à pour but de créer ou mettre à jour le trajet d'un utilisateur Mate, puis de regenerer les offres. L'execution de celui-ci vient mettre en file deux nouveaux jobs dans l'applicatif wqyzup-api

  • MateOffersUpdateWorker
  • MateFavoriteTripUpdateWorker

Le lancement des 2 jobs afin de déterminer les Matchs FavoriteRideRegenerateMatchingsWorker constitue 80% du temps d'execution, 5% sur l'appel du Garbage collector

FavoriteRideRegenerateMatchingsWorker

Comme il est possible de voir sur ce rapport newrelic. Ce worker déclenche des appels sur OSRM avec des temps de réponses en moyenne de 15ms à 20ms ayant pour cause latence du réseau pour une part qui semble non négligeable. 14% du taux d'execution est passé sur la communication avec ce service.

En suite c'est 20% du temps d'execution qui passé sur les SELECT et INSERT desressources de type FavoriteRidesMatching la table SQL comprends plus de 150_000 enregistements dont 50_000 avec le statut 'inactive'. Avec un RELEVANT_RIDE_SHARING_RATIO_THRESOLD de 0.45, il y a 60_000 FavoriteRidesMatching.relevant mais 25_000 Matchs relevant et inactif FavoriteRidesMatching.where(status: 'inactive').relevant.count. Il y a donc en base 23% de Matchs qui peuvent battir une offre concréte.

FavoriteRide

Il y a environ 600 FavoriteRide dont la distance est supérieure à 300 kilométres soit environ 3 heures de voiture, plus ou moins un Paris-Dijon

Points d'amélioration

Sidekiq

[3] pry(main)> Sidekiq::VERSION
=> "3.5.4"

Dans l'article optimizing sidekiq Mike Perham explique les améliorations qu'il apporte sur la version de Sidekiq 4 on peut voir une net amélioration de latence d'execution d'un job de 22ms à 10ms ainsi qu'une gestion plus fine du Garbage collector celui-ci ayayant un impact fort lors des purges sur le temps d'execution d'un job.

Benchmark

>  $ Benchmark.measure

From: /Users/chussenot/.rbenv/versions/2.3.1/lib/ruby/2.3.0/benchmark.rb @ line 291:
Owner: #<Class:Benchmark>
Visibility: public
Number of lines: 11

def measure(label = "") # :yield:
  t0, r0 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  yield
  t1, r1 = Process.times, Process.clock_gettime(Process::CLOCK_MONOTONIC)
  Benchmark::Tms.new(t1.utime  - t0.utime,
                     t1.stime  - t0.stime,
                     t1.cutime - t0.cutime,
                     t1.cstime - t0.cstime,
                     r1 - r0,
                     label)
end
[6] pry(main)> Benchmark.measure do Benchmark.measure {} end
=> #<Benchmark::Tms:0x007f82e263b318 @cstime=0.0, @cutime=0.0, @label="", @real=2.7899997803615406e-05, @stime=0.0, @total=0.0, @utime=0.0>

Même si l'impact reste faible sur l'execution d'une opération je ne conseille pas de garder ce type de chose pour un code en production, de plus il est souhaitable de réduire les écritures des logs au minimum et de ne pas mettre le log level sur INFO en production (Il ne pas possible de vérifier pour l'instant) mais sur le staging celui-ci est sur info LOG_LEVEL=info

Newrelic

Le middleware est actif dans l'environnement de :development mais le endpoint http://localhost:3003/newrelic n'est pas monté cf. newrelic.yml

Mettre en place celui-ci va permettre de d'inspecter les requettes SQL les plus lentes ainsi que les différents temps d'execution des fonctions avant de déployer sur le staging o la production.

Cache

Le cache dalli, n'est pas actif en staging et production c'est donc le cache de base avec écriture dans le dossier ./tmp qui est effectué.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment