Spark is used to perform in-memory transformations on customer data collected by Totango to generate analytics and insights. Luigi is used as a workflow engine to manage dependencies between batch processing tasks like metrics generation, health scoring, and alerting. The tasks are run on Spark and output to S3. A custom Gameboy controller provides monitoring and management of the Luigi workflow.
4. SaaS Customer Journey
DECREASE VALUE
DECREASE VALUE
CHURN
CHURN
GROW VALUE
FIRST VALUE
START
INCREASE USERS
INCREASE USAGE
EXPAND FUNCTIONALITY
CHURN
ONGOING VALUE
5. Customer Success Platform
●
Analytics for SaaS companies
●
Clear view of customer journey
●
Proactively prevent churn
●
Increase upsale
●
Track feature, module and total usage
●
Health score based on usages pattern
●
Improve conversion from trial to paying
9. About Totango
●
Founded in 2010
●
Size: ~50 (half R&D)
●
Offices in Tel Aviv, San Mateo CA
●
120+ customers
●
~70 million events per day
●
~1.5 billion indexed documents per month
●
Hosted on Amazon Web Services
11. Terminology
●
Service – Totango's customer (e.g. Zendesk)
●
Account – Service's (Zendesk's) customer
●
SDR (Service Data Record) – User activity
event (e.g. user Joe from account Acme did
activity Login in module Application)
12. SDR reception
●
Clients send SDRs to the gateway, where they
are collected, filtered, packaged and finally
stored in S3 for daily/hourly batch processing.
●
Realtime processing also notified.
14. Account Data Flow
1) Raw Data (SDRs)
2) Account Aging (MySQL - legacy)
3) Activity Aggregations (Hadoop – legacy)
4) Metrics (Spark)
5) Health (Spark)
6) Alerts (Spark)
7) Indexing to Elasticsearch
15. Data Structure
●
Account documents stored on Amazon S3
●
Hierarchial directory structure per task param:
e.g. /s-1234/prod/2015-04-27/account/metrics
●
Documents have a predefined JSON schema.
JSON mapped directly to Java document class
●
Each file is an immutable collection of documents
One object per line – easily partitioned by lines
17. Resilient Distributed Datasets
●
RDDs – distributed memory abstraction that lets
programmers perform in-memory computations
on large clusters in a fault-tolerant way
●
Initial RDD created from stable storage
●
Programmer defines a transformation from an
immutable input object to a new output object
●
Transformation function class can (read: should!)
be built and tested separately from Spark
18. Transformation flow
Read: inputRows = sparkContext.textFile(inputPath)
Decode: inputDocuments = inputRows.map(new
jsonToAccountDocument())
Trasform: docsWithHealth = inputDocuments.map(new
augmentDocumentWithHealth(healthCalcMetadata))
… other transformations may be done, all in memory …
Encode: outputRows = docsWithHealth.map(new
accountDocumentToJson())
Write: outputRows.saveAsTextFile(outputPath)
19. Examples (Java)
Class AugmentDocumentWithHealth implements
Function<AccountDocument, AccountDocument>
AccountDocument call(final AccountDocument document)
throws Exception { … return document with health … }
Class AccountHealthToAlerts implements
FlatMapFunction<AccountDocument, EventDocument>
Iterable<EventDocument> call(final AccountDocument
document) throws Exception { … generate alerts … }
20. Transformation function
●
Passed as parameter to Spark transformation:
map, reduce, filter, flatMap, mapPartitions
●
Can (read: should!!) be checked in Unit Tests
●
Serializable – sent to Spark worker serialized
●
Function must be idempotent!
●
May be passed immutable metadata
22. Why a workflow engine?
●
Managing many ETL jobs
●
Dependencies between jobs
●
Continue pipeline from point of failure
●
Separate workflow per service per date
●
Overview and drill-down status Web UI
●
Manual intervention
23. Workflow engines
●
Azkaban, by LinkedIn (mostly for Hadoop)
●
Oozie, by Apache (only for Hadoop)
●
Amazon Simple Workflow Service (too generic)
●
Amazon Data Pipeline (deeply tied to AWS)
●
Luigi, by Spotify (customizable) – our choice!
24. What is Luigi
●
Like Makefile – but in Python, and for data
●
Dependencies are managed directly in code
●
Generic and easily extendable
●
Visualization of task status and dependency
●
Command-line interface
36. Gameboy
●
Totango-specific controller for Luigi
●
Provides high level overview
●
Enable manual re-run of specific tasks
●
Monitor progress, performance, run time,
queue, worker load etc
40. Summary
●
Typical data flow – from raw data to insights
●
We use Spark for fast in-memory
transformations, all code is in Java
●
Our batch processing pipeline consist of a
series of tasks, which are managed in Luigi
●
We don't use all of Luigi's python abilities, and
we've added some new management abilities
בחברות SaaS, אינם קונים רשיון אלא משלמים עבור שימוש.הם יישארו וישלמו כל עוד הם מרוצים, אך בכל שלב הם גם יכולים לעזוב בפתאומיות ולא לחדש את המנוי.
כדי למנוע עזיבה, צריך להבין את מצב הלקוח, לפני שהוא קיבל את ההחלטה לעזוב כי אז כבר מאוחר מדי.
גם ללקוח קיים שאינו חושב לעזוב, אפשר לעזוב לנצל את המוצר טוב יותר, להגדיל את אפשרויות השימוש שלו, להרחיב את המנוי וכו&apos;.
תחום ה-Customer Success הוא ניהול האינטראקציה עם הלקוחות בכל שלבי המסע שלהם.
אז מה אנחנו מאפשרים?
כלי ניתוח עבור חברות המספקות תוכנה כשירות.
הצגה ברורה של מסע הלקוח, החל מהשלב שהוא מתנסה במוצר, כשהוא רוכש מנוי, כיצד הוא מפיק ערך, ועוד
גילוי ירידה בשימוש לפני שהדבר הופך להחלטה לעזוב
הגדלת מכירות של תכונות נוספות שהלקוח יפיק מהן ערך
מעקב אחר שימוש ברמת תכונה, מודול או בסיכום כללי
מדד בריאות המוגדר על בסיס דפוסי השימוש במוצר
שיפור ההמרה של לקוחות מתנסים למשלמים
ככה נראה ה-Health Console שלנו
ניתן לראות סיכום של הלקוחות, לפי סגמנטיםלמשל לקוחות חדשים, לקוחות גדולים, וכו&apos;
ניתן לעקוב אחר שימוש במודולים שונים במערכת, על פני הזמן ובחלוקות לפי פרמטרים שונים
אנחנו עושים dogfooding ומשתמשים במערכת שלנו על עצמנו
כשאנחנו מכניסים פיצ&apos;ר חדש, אנחנו מודדים את השימוש של חלקים שונים בו כדי להבין כיצד משתמשים בו, האם יש חלקים שחוששים להשתמש בהם או לא ברור איך
הגרף מראה את הסיכום הכללי, וניתן ללחוץ ולהכנס לרשימת הלקוחות המפורטת ולראות מי השתמש במה
קצת על Totango
קיימת כ-5 שנים
מונה כ-50 אנשים, אנשי הפיתוח בארץ והמכירות בחו“ל
עשרות מליוני אירועים ביום
מיליארדי מסמכים מאונדקסים מדי חודש
כל התשתיות בענן של Amazon
קצת על האריכטקטורה שבעזרתה המידע הגולמי אודות השימוש שנשלח אלינו למערכת הופך למידע שימוש אודות הלקוחות של הלקוחות שלנו
שירות – לקוח שלנו
חשבון – לקוח של הלקוח שלנו
אס די אר – המידע שנשלח כאשר משתמש אצל הלקוח של הלקוח שלנו, מבצע פעולה במערכת של הלקוח שלנו
האופן שהמידע זורם למערכת שלנו די סטנדרטי – נכנס מבחוץ לשרת gateway
שם הוא עובר עיבוד ראשוני, ואז נארץ בקבצים על גבי S3
אני מניח שזה דפוס שימוש טיפוסי שבו משתמשים רבים על התשתית הזאת, נכון?
מדי יום, וחלקית גם מדי שעה, המידע שהגיע עובר תהליך עיבוד
התהליך מאותחל ע“י Jenkins שזו מערכת תזמון נפוצה
את התהליך עצמו מנהל luigi שעליו ארחיב בהמשך
עיבוד המידע נעשה ע“י Spark, וחלקית ע“י מערכות ישנות שעובדות על כלים אחרים
לבסוף המידע מאונדקס ב-Elasticsearch ומשם מוצג בממשק המשתמש של Totango
בקצרה – השלבים שעובר המידע אצלנו במערכת
מתחילים במידע גולמי
מעיפים חשבונות שאינם פעילים
סוכמים את הפעילויות שמשתמשים עשו
מחשבים מטריקות שונות על הנתונים
מחשבים את מדד הבריאות על כל חשבון
מייצרים התראות במקרה של שינויים הדורשים התייחסות
ולבסוף כל המידע כאמור נשמר באינדקס
המידע נשמר בפורמט JSON שממופה ישירות למבנים שאנחנו עובדים איתם בקוד
נשמר בקבצי טקסט שבהם כל שורה היא אובייקט JSON
הקבצים נשמרים במבנה תיקיות הירארכי ב-S3
אז איך בעצם המידע עובר את העיבוד?
ספארק מבוסס על הפשטה של עיבוד המידע
פשוט: טוענים את המידע ממקור המידע
ומגדירים סדרה של טרנספורמציות על המידע, כלומר סדרה של הוראות – איך להפוך את הקלט לפלט
לדוגמה – זה הקוד שלנו שמעשיר את האובייקט של חשבון עם מדד הבריאות שלו
בהתחלה טוענים את הרשומות מתוך הקובץ וממירים את שורות הטקסט לאובייקט ב-java
אז קוראים לטרנספורמציה map ומעבירים לה פונקציה
הפונקציה הזו, כפי שנראה תיכף, צריכה לדעת לעשות דבר אחד פשוט – לקחת רשומה בקלט ולהחזיר רשומה בפלט
אפשר לבצע עוד טרנספורמציות כאלה והכל ייעשה בזכרון, זאת לעומת hadoop למשל שחייב בכל שלב לקרוא ולכתוב לדיסק
לבסוף כותבים את מידע הקלט בחזרה לדיסק, או ליעד מידע אחר כלשהו
לדוגמה
פונקציה שמעשירה חשבון עם בריאות מקבלת ומחזירה אובייקט של חשבון
פונקצייה שמייצרת התראות מקבל חשבון, ומחזירה רשימה של התראות
הפונקציות הללו מועברות כפרמטר לטרנספורציות של ספארק
את הפונקציה אפשר וכדאי לבדוק ב-Unit test, אין צורך להרים ספארק כדי לבדוק את הלוגיקה ואפשר לעשות זאת בנפרד לחלוטין (למעשה כך עשינו אצלנו)
הפונקציה נשלחת אל המידע ולא המידע אל הפונקציה, לכן הפונקציה צריכה להיות ניתנת לאריזה
על הפונקציה להיות אידמפוטנטית – כלומר להחזיר את אותו פלט בהינתן אותו קלט, בלי קשר לכמות ההרצות – בקיצור אסור לה לשנות state חיצוני כלשהו!
ניתן להעביר לפונקציה ביצירה מידע כלשהו שהוא לקריאה בלבד
אז יש לנו הרבה לקוחות, הרבה משימות עיבוד, איך מנהלים את כל זה?
למה בכלל צריך מערכת לניהול זרימת העבודה?
צריך לנהל הרבה משימות
יש תלויות בין המשימות
כשלון משימות הוא עניין של זמן, צריך לדעת להמשיך מאותו מקום בו השרשרת נכשלה
להפריד בצורה ברורה בין הרצות לתאריכים שונים וללקוחות שונים
לצפות בצורה קלה על המצב של המשימות, וגם להכנס לפרטים כדי להבין למה משימה נתקעה או נכשלה
להתערב ידנית במקרה של טיפול בתקלה או פעילות יזומה
בחנו כמה אפשרויות, חלקכם בטח שמעתם עליהם
כמה כבר שאלו אותי לגבי אלה
לא נתעכב עליהם
לאחר סקירה של מערכות אלה ועוד, אנחנו בחרנו את Luigi
אז מה זה לואיג&apos;י?
כמו קבצי הגדרות לבניית קוד תוכנה, רק שזה בפייטון ובשביל טיפול במידע ולא בניית קוד
התלויות מנוהלות ישירות בקוד ולא בקובץ הגדרות
כללי וניתן בקלות להרחבה
ניתן לראות בצורה ויזואלית את המצב של המשימות והתלויות ביניהן
יש גם ממשק בשורת פקודה, כך שזה נותן למפתחים להריץ משימה ישירות לפי הפרמטרים שלה
בשביל להגדיר משימה בלואיג&apos;י, צריך להגדיר לה:
מה הקלט
מה הפלט
במה היא תלוייה
ומה הקוד עצמו שמתבצע כשהיא רצה
הנה דוגמא מ-Spotify
המשימה תלויה במשימה קודמת שנקראת Streams
היא פותחת קובץ, מבצעת עיבוד וכותבת לקובץ המטרה
אפשר להשתמש בקלאסים אחרים שכבר יודעים לעשות דברים נפוצים, ואז רק צריך להגדיר להם את הפרמטרים המתאימים
למשל ממשק ל-hadoop, spark, elasticsearch, hive, וכו&apos;
אפשר להעביר פרמטרים למשימה, והם יועברו למשתנה בקוד
אפשר בקלות להריץ את המשימה בשורת הפקודה גם בלי השרת של luigi ולראות איך היא מתנהגת
אפשר לראות בקלות אילו משימות מתבצעות עכשיו, מה הפרמטרים שלהן ומה הססטוס שלהן
אפשר גם לראות גרף תלויות שמראה כל משימה ובמי היא תלויה או מי תלוי בה, ומה הסטטוסס הנוכחי שלהן
למשל כאן רואים משימות ירוקות שסיימו, משימה בכחול שכרגע רצה ומשימות צהובות שמחכות לה
אם משימה כלשהי הייתה נכשלת, היינו רואים אותה באדום ואפשר היה ללחוץ על העיגול האדום כדי לקבל פרטים על הכשלון
כאן רואים גרף עם הרבה יותר תלויות
אז איך אנחנו משתמשים ב-Luigi אצלנו ב-Totango?
קודם כל, כל הקוד שלנו הוא ב-Java
אנחנו משתמשים ב-luigi על מנת לנהל את התהליך והתלויות, אבל ההרצה עצמה בעצם קוראת לקוד שלנו ב-java ומעבירה לו את הפרטמרטים הרלוונטיים
לואיג&apos;י יודע לנהל את המשימות והתלות ביניהן, אך בהתחלה בשביל ליזום את ההרצה אנחנו משתמשים ב-Jenkins
יש לנו כלי פנימי שנקרא Gameboy
הוא מאפשר לנו לראות בצורה טובה יותר מידע רלוונטי אלינו אודות התהליכים שלנו שרצים במערכת
וגם מאפשר לנו להריץ מחדש תהליכים מסוימים עם פרמטרים מסוימים
כך נראה המסך הראשי
ניתן לראות מי רץ כרגע ומה הסטטוס שלו
ניתן לראות גם סטטוס לתאריך מסוים
כאן רואים גרפים אודות כמה תהליכים רצים בכל מיני שעות על פני היום או השבוע
וגם ניתן לראות ללקוחות שלנו מי כבר סיים עיבוד מידע ליום מסויים
ניתן לבחור לקוח, לבחור תאריך, ולהריץ עליו את תהליך ה-batch החל משלב מסויים
אנחנו עושים זאת במקרה של תקלות, או כאשר רוצים לעדכן נקודתית מידע מסוים