Just-Tech-IT
Published in

Just-Tech-IT

Détection des abandonnistes avec ksqlDB

Préambule

  • (cas 1) un parcours métier à base de formulaires où le retour arrière navigateur déclenche une remise à 0 des entrées de la page précédente. Si l’utilisateur doit resaisir toutes ces données ne doit-on pas remettre à 0 le chrono du Δmax?
  • (cas 2) un utilisateur qui ferme sa page par erreur mais revient dans le parcours, comment peut-on le traiter pour qu’il ne soit pas considéré comme un abandonniste s’il finit son parcours dans les temps?
  • (cas n) et bien d’autres cas à la marge encore…

Proof Of Concept

Introduction

Copyright © Confluent, Inc. 2014–2020

Do It Yourself

Environnement

Copyright © Confluent, Inc. 2014–2020
  • kafka, évidemment
  • zookeeper, malheureusement encore ;)
  • ksqldb-server, l’engine nécessaire à l’exécution des requêtes SQL-like
  • ksqldb-cli, la command line interface qui va nous permettre de construire ensemble notre topologie de stream.
  • kafkahq, renommé dernièrement AKHQ, par confort et habitude dans les étapes de vérification de mon exercice. Et aussi parce que c’est développé avec passion par un français proche de chez moi et que c’est un outil qui mérite d’être connu ;-)

Design

{
"eventId": "5ece8acbf9269864aea2c080",
"eventDateTime": "2020-05-28T10:00:00.000+02:00",
"actorId": "yellow",
"state": "start|stop"
}
Représentation de mon jeu de données
  • Le cas jaune est intéressant puisqu’il représente un faux-positif d’abandonniste. Pour le moment, j’accepte ce faux-positif et considère qu’il est le fruit d’un Δmax mal calibré.
  • Le cas violet représente un acteur qui n’est pas revenu en arrière et qui a terminé son parcours dans les temps. Il n’est pas abandonniste.
  • Le cas orange représente un acteur qui a fait un retour arrière et n’a pas terminé son parcours. Il est abandonniste.
  • Le cas vert représente un acteur qui a fait un retour arrière et qui a terminé son parcours dans les temps. Il n’est pas abandonniste puisque le Δmax a été réinitialisé lors de son retour arrière.
Topologie de streaming

Allez on pratique

docker-compose up -d
docker run --tty --mount type=bind,source=/input,target=/input --network abandonnistes_default confluentinc/cp-kafkacat kafkacat -b kafka:39092 -t userActivity -P -l -K: /input/userActivity.json
docker-compose exec ksqldb-cli ksql http://ksqldb-server:8088
CREATE STREAM stream_userActivity
(
eventId STRING,
eventDateTime STRING,
actorId STRING,
state STRING
)
WITH
(
KAFKA_TOPIC='userActivity',
VALUE_FORMAT='JSON',
TIMESTAMP='eventDateTime',
TIMESTAMP_FORMAT='yyyy-MM-dd''T''HH:mm:ss.SSSXXX'
);
Message
----------------
Stream created
----------------
CREATE TABLE table_leaverDetection
WITH (KAFKA_TOPIC = 'leaverDetection', VALUE_FORMAT='JSON')
AS SELECT
actorId,
TIMESTAMPTOSTRING(WINDOWSTART,'yyyy-MM-dd''T''HH:mm:ss.SSSXXX') as windowStartTime,
TIMESTAMPTOSTRING(WINDOWEND,'yyyy-MM-dd''T''HH:mm:ss.SSSXXX') as windowEndTime,
(WINDOWEND-WINDOWSTART)/1000 AS windowDurationSeconds,
LATEST_BY_OFFSET(state) AS lastState
FROM stream_userActivity
WINDOW SESSION (5 MINUTES)
GROUP BY actorId
HAVING COUNT_DISTINCT(state) = 1
EMIT CHANGES;
Message
----------------------------------------------------
Created query with ID CTAS_TABLE_LEAVERDETECTION_0
----------------------------------------------------
Select * from table_leaverDetection EMIT CHANGES;-------+-----------------+-----------------+-----------+----------+
ACTORID|WINDOWSTARTTIME |WINDOWENDTIME |WINDOWDURA |LASTSTATE |
| | |TIONSECONDS| |
-------+-----------------+-----------------+-----------+----------+
yellow |2020-05-28T08:00:|2020-05-28T08:00:|0 |start |
|00.000Z |00.000Z | | |
orange |2020-05-28T08:05:|2020-05-28T08:07:|120 |start |
|00.000Z |00.000Z | | |
yellow |2020-05-28T08:09:|2020-05-28T08:09:|0 |stop |
|00.000Z |00.000Z | | |
Press CTRL-C to interrupt
  • Solution du perfectionniste: On peut complexifier la topologie de stream actuelle, en calculant d’autres agrégats en utilisant par exemple d’autres techniques de windowing, et en refaisant des jointures assez complexes. Allez lire la documentation, c’est passionnant.
  • Solution du “le mieux est l’ennemi du bien”: On peut considérer qu’il suffit de mieux calibrer le Δmax, en acceptant éventuellement de perdre en réactivité sur la capacité à prendre une action suite à la détection. C’est un trade-off acceptable pour garder une solution simple.
SELECT
actorId,
TIMESTAMPTOSTRING(WINDOWSTART,'yyyy-MM-dd''T''HH:mm:ss.SSSXXX') as windowStartTime,
TIMESTAMPTOSTRING(WINDOWEND,'yyyy-MM-dd''T''HH:mm:ss.SSSXXX') as windowEndTime
FROM stream_userActivity
WINDOW SESSION (30 MINUTES)
GROUP BY actorId
HAVING COUNT_DISTINCT(state) = 1
EMIT CHANGES;
+---------+-------------------------+-------------------------+
|ACTORID |WINDOWSTARTTIME |WINDOWENDTIME |
+---------+-------------------------+-------------------------+
|orange |2020-05-28T08:05:00.000Z |2020-05-28T08:07:00.000Z |
Press CTRL-C to interrupt

Perspectives

https://www.youtube.com/watch?v=u-qVBekTcR4

--

--

Sharing knowledge and best practices for valuable tech workers. This blog is Powered by @AXAJobs_fr

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store