xee-lab / xee-api-docs

This repository contains all the docs of the Xee platform and tools provided by Xee tech team
https://dev.xee.com
Other
11 stars 7 forks source link

[Cars] locations/signals : Comment gérer la pagination ? #47

Open josephpage opened 8 years ago

josephpage commented 8 years ago

Sur les endpoints /cars/{id}/locations et /cars/{id}/signals on peut ajouter un paramètre limit, ce qui permet de faire à peu près le même effet que de la pagination, mais dans le résultat que retourne l'API on ne voit pas si ce paramètre a eu un effet... ou pas.

On ne peut donc pas savoir si il est nécessaire de faire une requête supplémentaire pour avoir toutes les datas sur la période de temps initialement demandée.

A minima, un header HTTP serait utile pour donner le nombre total de résultats : Xee-Limit-Total-Results: 350000 (350 000 est un cas réel sur un de nos véhicules)

Idéalement, vous pourriez fournir un header Link pour donner l'url de la page suivante (best practice des API REST), en conservant les paramètres de la requête initiale. Il suffirait juste de modifier le paramètre end pour qu'ils ne sélectionnent pas les locations déjà retournés :

Link: <https://cloud.xee.com/api/v3/cars/{carId}/locations
   ?begin={begin de la requête initiale}
   &end={date de la plus ancienne location parmi les résultats, ou n+1 pour l'exclure}
   &limit={limit de la requête initiale}>; rel="next"

Avez-vous d'autres solutions en tête ? Comment faire en attendant ?

quentin7b commented 8 years ago

Bonjour Joseph,

Sur nos APIs le paramètre limite est plutôt là pour éviter une surcharge que pour gérer la pagination. Nous n'étions pas certains de le garder à la base.

Aujourd'hui, nous pensons qu'une pagination de date à date permet de rentre l'API plus intuitive dans bon nombre de cas. Cette limite sert par exemple à obtenir les 10 dernières positions GPS connues.

En revanche l'idée d'un Header pour indiquer le nombre total de résultats a du sens. Nous prenons note.

josephpage commented 8 years ago

Une pagination date à date est effectivement très intuitive... ou pas.

Le propre d'une pagination c'est d'avoir des packets de données limités et constants. x données par x données, jusqu'à la dernière page où on obtient x ou moins.

Avec le fonctionnement actuel on a 2 techniques possibles :

  1. faire une requête x(1) avec une limite y, puis refaire la même requête x(2) avec la même limite y mais avec un begin correspond à la date du dernier signal de la requête x(1)
  2. trouver l'interval de temps minimal pour lequel on obtient le nombre maximal de valeurs qu'on souhaite. Dans notre cas, on a trouvé qu'en faisant les requêtes jour par jour, notre backend arrive à s'en sortir

Problèmes :

  1. a. on rate systématiquement les signaux qui ont le même timestamp que la dernière valeur de la requête précédente, ou il faut mettre en place un contrôle de déduplication de données (très couteux vu le nombre de données)
  2. b il faut penser à gérer le chevauchement des mois...
  3. on peut avoir y signaux ou 0 signaux, c'est imprévisible, et pire, on ne peut pas connaître la fin avant d'avoir tout essayé, puisque l'absence de signaux ne signifie pas la fin des résultats

En tout cas très intéressé pour une astuce à ce sujet ;)

denouche commented 7 years ago

Juste pour apporter quelques éléments: On peut utiliser des headers HTTP standards plutôt que d'en inventer d'autres pour la pagination. Le header Range permet dans une request de demander seulement une partie de la ressource, et en réponse on peut imaginer un status HTTP 206 (Partial content), et un header content-range.

quentin7b commented 7 years ago

Pour le coup, j'ai du mal à saisir le problème.

Si la requête jour/jour est faite a 23:59:59:999 les chances de "rater" des signaux de la journée qui vient de passer sont vraiment proches de 0 non ?

josephpage commented 7 years ago

@denouche Le HTTP 206 est surtout utilisé pour le streaming de fichier lourds (images/vidéos/zip/tar) pour lesquels on veut que le téléchargement soit résistant aux interruptions. Le header Link est un standard (RFC-5988). L'API Github est souvent nommée comme une des meilleurs implémentations de pagination.

@quentin7b En effet avec une requête jour/jour faite a 23:59:59:999 on a très très peu de chance de rater des signaux.

Cas d'usage où une pagination basique est essentielle :

Je veux faire l'analyse d'un véhicule sur les données des 2 dernières années. Disons que pour les 2 ans, cela représente 1 million de lignes de données (locations+signals) soit 42 000 lignes/mois malheureusement la capacité de buffering de mon serveur n'est que de 10000 lignes

  • si je fais des requêtes par mois, je risque de dépasser ma capacité de buffering
  • si je fais des requêtes par semaine, pareil
  • si je fais des requêtes par journée, je risque de faire beaucoup de requêtes pour rien si le véhicule n'a pas roulé, ou peut-être dépasser ma capacité de buffering si le véhicule a beaucoup roulé
  • si je fais des requêtes par heure, pas de soucis en vue mais ... 24x365x2 = 17520 ... OMG!

L'impossibilité de prédire la taille de la réponse est problématique.

josephpage commented 7 years ago

Une autre solution pour faciliter le traitement des résultats locations/signals seraient de retourner les données dans un format CSV. Si 1 ligne = 1 donnée, il très facile de garder l'ensemble du résultat sous une forme peu consommatrice en mémoire, et d'itérer à son rythme sur toutes les lignes.

Pour les locations :

{timestamp},{latitude},{longitude},{altitude},{satellites},{heading}

exemple :

1456799060000,50.67815,3.208155,31.8,4,167

Pour les signals :

{timestamp},{name},{value}

exemple :

1456799060000,LockSts,0

Putting the timestamp first allows quick filter/sort/inverse by date, before parsing ;)