הזרמת נתונים
הזרמת נתונים מעבירה אירועים מ־Joryio ליעדים חיצוניים כמעט בזמן אמת. כל זרם מקשר בין יעד לקבוצת אירועים, ו־Joryio מוסרת ליעד את האירועים התואמים באצוות.
עברו אל Data → Data Streaming כדי לנהל את הזרמים שלכם.
יעדים
| יעד | סוג | מה הוא עושה |
|---|---|---|
| Webhook | נקודת קצה של HTTP | שליחת כל אירוע בבקשת POST כאובייקט JSON לכל כתובת HTTPS |
| Snowflake | מחסן נתונים | טעינה רציפה של אירועים לטבלת Snowflake |
| BigQuery | מחסן נתונים | הזרמת אירועים למערך נתונים ב-BigQuery |
| Amazon Redshift | מחסן נתונים | טעינת אירועים לאשכול Amazon Redshift |
| Amazon S3 | אחסון אובייקטים | כתיבת קובצי JSON / NDJSON באצוות ל־Bucket |
| Kafka | זרם אירועים | פרסום אירועים ב־topic של Kafka |
| Mixpanel | אנליטיקת מוצר | העברת אירועים לפרויקט Mixpanel |
| Amplitude | אנליטיקת מוצר | העברת אירועים לפרויקט Amplitude |
שדות חיבור
לכל יעד יש פרטי חיבור משלו. נתונים סודיים (סיסמאות, מפתחות, טוקנים, JSON של חשבון שירות וערכי כותרות של Webhook) מוצפנים בעת האחסון ולעולם לא מוצגים שוב - ראו הרשאות מינימליות לפרטי גישה.
- Webhook - כתובת URL, סוד לחתימה (אופציונלי), כותרות נוספות (אופציונלי, אובייקט JSON כגון
{"Authorization": "Bearer <token>"}; הערכים מטופלים כסודות) - Snowflake - Account, Username, Password, Role (אופציונלי - התפקיד שבו רצות הפקודות; כשהוא ריק, חל תפקיד ברירת המחדל של המשתמש), Warehouse, Database, Schema, Table
- BigQuery - Project ID, Dataset ID, Table ID, Service Account JSON
- Amazon Redshift - Host, Port, Database, User, Password, Schema, Table
- Amazon S3 - Bucket, Prefix, Region, Endpoint (אופציונלי, לאחסון תואם-S3), Access Key ID, Secret Access Key
- Kafka - Brokers, Topic, SASL Username/Password, SSL
- Mixpanel - Project Token, Region (US / EU / India)
- Amplitude - API Key, Region (US / EU)
הרשאות מינימליות לפרטי גישה
תנו לכל זרם פרטי גישה משלו, שיכולים לעשות דבר אחד בלבד: לכתוב לטבלה, לנתיב בדלי או לנושא שאליו הזרם מוסר. אם פרטי הגישה ידלפו אי פעם, זה כל מה שאפשר לעשות איתם - אי אפשר לקרוא בעזרתם את הנתונים שלכם או לגעת בשום דבר אחר. הדוגמאות שלהלן משתמשות בשמות לדוגמה (example-bucket, analytics.joryio_events, joryio-events); החליפו אותם בשמות שלכם.
איך Joryio שומרת פרטי גישה
- מוצפנים. סיסמאות, מפתחות, טוקנים, JSON של חשבון שירות וערכי כותרות של Webhook מוצפנים לפני שהם נשמרים.
- לעולם לא מוצגים שוב. העורך וה-API מציגים סוד שמור כ-
********. אף אחד - כולל אתם וצוות Joryio - לא יכול לקרוא אותו בחזרה בלוח הבקרה או ב-API. - נשמרים כשעורכים שדות אחרים. שדה סוד שמשאירים ריק (או
********) שומר על הערך השמור; רק ערך שמקלידים מחליף אותו. שינוי שם של זרם או של האירועים שלו לעולם לא נוגע בפרטי הגישה. - נמחקים יחד עם הזרם. מחיקת זרם מוחקת את פרטי הגישה השמורים שלו. כשהחשבון נמחק, הם נמחקים יחד עם שאר נתוני החשבון.
כשמוחקים זרם, בטלו או החליפו גם את פרטי הגישה שלו ביעד: ל-Joryio כבר אין אותם, אבל המפתח עצמו נשאר בתוקף עד שתבטלו אותו.
גם כותרות Webhook הן סודות. הזינו אותן כאובייקט JSON, למשל {"Authorization": "Bearer <token>"}. שמות הכותרות נשארים גלויים; כל ערך מוצפן ומוצג כ-********. בעריכת הזרם, השאירו ******** כדי לשמור ערך, הקלידו ערך חדש כדי להחליף אותו, או הסירו כותרת כדי למחוק אותה. השתמשו בטוקן שהונפק ל-Joryio בלבד, כך שתוכלו לבטל אותו בלי להשפיע על שום דבר אחר.
Amazon S3
צרו משתמש IAM (או תפקיד) עבור Joryio שההרשאה היחידה שלו היא s3:PutObject על הדלי והקידומת של הזרם, והשתמשו במפתח הגישה שלו בזרם. Joryio כותבת אובייקטים תחת <prefix>/<date>/, כך שעם הקידומת joryio-events:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "JoryioWriteEvents",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::example-bucket/joryio-events/*"
}
]
}
בדיקת חיבור מוודאת שהדלי קיים, ו-AWS מתירה זאת רק עם s3:ListBucket על הדלי. הרשאה זו מאפשרת למפתח לראות את שמות האובייקטים בדלי, ולכן היא אופציונלית: בלעדיה המסירה עובדת ורק הבדיקה מדווחת על כישלון. כדי לאפשר את הבדיקה, הוסיפו את ההצהרה הבאה:
{
"Sid": "JoryioTestConnection",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-bucket"
}
לאחסון תואם-S3, צרו טוקן המוגבל לדלי האחד עם הרשאת כתיבת אובייקטים בלבד.
Amazon Redshift
צרו משתמש מסד נתונים ייעודי שיכול רק להוסיף שורות לטבלה של הזרם:
CREATE USER joryio_loader PASSWORD '<strong-password>';
GRANT USAGE ON SCHEMA analytics TO joryio_loader;
GRANT INSERT ON TABLE analytics.joryio_events TO joryio_loader;
הזינו joryio_loader בשדה User של הזרם.
Snowflake
צרו תפקיד שיכול רק להוסיף שורות לטבלה של הזרם (בתוספת הרשאות USAGE ש-Snowflake דורשת כדי להגיע אליה), ומשתמש שיש לו את התפקיד הזה:
CREATE ROLE JORYIO_WRITER;
GRANT USAGE ON WAREHOUSE LOAD_WH TO ROLE JORYIO_WRITER;
GRANT USAGE ON DATABASE ANALYTICS TO ROLE JORYIO_WRITER;
GRANT USAGE ON SCHEMA ANALYTICS.EVENTS TO ROLE JORYIO_WRITER;
GRANT INSERT ON TABLE ANALYTICS.EVENTS.JORYIO_EVENTS TO ROLE JORYIO_WRITER;
CREATE USER JORYIO_LOADER PASSWORD = '<strong-password>'
DEFAULT_ROLE = JORYIO_WRITER DEFAULT_WAREHOUSE = LOAD_WH;
GRANT ROLE JORYIO_WRITER TO USER JORYIO_LOADER;
הזינו JORYIO_LOADER בשדה Username ו-JORYIO_WRITER בשדה Role. השדה Role אופציונלי: כשהוא מוגדר, Joryio מריצה כל פקודה בתפקיד הזה; כשהוא ריק, חל תפקיד ברירת המחדל של המשתמש.
BigQuery
צרו חשבון שירות עבור Joryio והעניקו לו BigQuery Data Editor (roles/bigquery.dataEditor) על מערך הנתונים של הזרם בלבד - בהגדרות השיתוף של מערך הנתונים, לא ברמת הפרויקט. לאחר מכן צרו עבורו מפתח JSON והדביקו אותו בשדה Service Account JSON.
Joryio מזרימה שורות באמצעות ה-API של BigQuery להוספת שורות, וקוראת את המטא-נתונים של הטבלה כדי לבדוק את החיבור; אף אחד מהם לא מריץ משימת שאילתה, ולכן אין צורך ב-BigQuery Job User. הרשאת Data Editor על מערך נתונים מאפשרת לשנות גם טבלאות אחרות בו, ולכן שמרו את טבלת הזרם במערך נתונים משלה, או העניקו תפקיד מותאם עם bigquery.tables.get ו-bigquery.tables.updateData בלבד על הטבלה.
Kafka
צרו משתמש SASL עבור Joryio (Joryio מזדהה באמצעות SASL/PLAIN) והתירו לו רק WRITE ו-DESCRIBE על הנושא של הזרם. צרו את הנושא בעצמכם מראש - Joryio לא צריכה הרשאה ליצור נושאים.
kafka-acls --bootstrap-server <broker:port> --command-config admin.properties \
--add --allow-principal User:joryio \
--operation Write --operation Describe \
--topic joryio-events
Mixpanel, Amplitude ו-Webhooks
- Mixpanel - ה-Project Token יכול רק לשלוח אירועים לפרויקט שלו; הוא לא יכול לקרוא נתונים. השתמשו בטוקן של הפרויקט שהזרם אמור להזין.
- Amplitude - ה-API Key הוא מפתח הקליטה של פרויקט אחד. השתמשו בפרויקט שהזרם אמור להזין.
- Webhook - השתמשו בסוד חתימה ובטוקן
Authorizationשהונפקו ל-Joryio בלבד, וקבלו רק את מה שהזרם שולח.
יצירת זרם
- עברו אל Data → Data Streaming ולחצו על Create Stream.
- הזינו Name לזרם (השם יוצג ברשימת הזרמים) ובחרו destination.
- מלאו את שדות ה־connection של היעד.
- בחרו payload format - JSON (אובייקט אחד לכל אירוע) או NDJSON (אירוע אחד בכל שורה, כשהאירועים מופרדים בשורות חדשות).
- בחרו אילו events להזרים (ראו בהמשך).
- בדקו את שורת הסיכום ולחצו על Create Stream.
בחירת אירועים
אתם קובעים בדיוק אילו אירועים הזרם מקבל:
- כל האירועים - הפעילו כדי להזרים כל אירוע קיים ועתידי. כבו כדי לבחור אירועים מסוימים.
- הודעות, לפי ערוץ - אירועי הודעות נבחרים לכל ערוץ בנפרד (ראו בחירה לפי ערוץ).
- קבוצות אחרות - In-App, מסעות, קמפיינים, כרטיסי Wallet, מסחר אלקטרוני וזהות. סמנו קבוצה שלמה או אירועים בודדים.
- אירועים מותאמים אישית - תחת Custom Event, חפשו או הקלידו אירועים שאתם עוקבים אחריהם (אלה שנשלחים עם
track(), כמוpurchaseאוpage_view). הבורר מציג את האירועים שסביבת העבודה שלכם באמת תיעדה; אפשר גם להקליד אירוע שעדיין לא תועד וללחוץ Add.
בחירה לפי ערוץ
העורך מציג אזור לכל ערוץ - דוא"ל, SMS, WhatsApp, פוש, Viber ו-Webhook - ובו רק האירועים שקיימים בערוץ הזה (אין "פתיחת SMS"). בתוך כל אזור האירועים מקובצים:
| קבוצה | אפשרויות |
|---|---|
| מעורבות | שליחה, מסירה, פתיחה (דוא"ל), קריאה (WhatsApp), הקלקה |
| בעיות | החזרה, סימון כספאם, כישלון, לא נשלחה, דחייה, מענה אוטומטי (דוא"ל) |
| הסכמה | הסרה מרשימה, הרשמה |
| נכנסות | הודעה נכנסת (SMS, WhatsApp, Viber) |
לכל אזור יש תיבת בחירת הכול משלו, ו-בחירת הכול בכל הערוצים מסמנת הכול בבת אחת. ליד כל אפשרות מוצגת הערכת נפח חודשית - כמה אירועים כאלה סביבת העבודה שלכם תיעדה ב-30 הימים האחרונים - כדי שתראו לפני השמירה מה הבחירה תעלה ליעד.
שליחה, מסירה ופתיחה מסומנות נפח גבוה: כל אחת היא שורה אחת לכל הודעה. זרם חדש מתחיל עם כל שאר האפשרויות מסומנות, ושלוש אלה נשארות לבחירתכם.
פורמט הנתונים לא משתנה. סימון מסירה תחת SMS מזרים את האירוע הרגיל message.delivered שבו המאפיין channel הוא sms - אותו שם ואותם ערכים שמאגר האירועים, ה-REST API ואינטגרציות ה-Webhook משתמשים בהם. הודעות נכנסות שומרות על שמותיהן: sms.message.received, whatsapp.message.received ו-viber.message.received.
זרם שנוצר דרך ה-API עדיין יכול לבחור אירוע הודעה לפי שם ("events": ["message.delivered"], ואפשר לצמצם עם "channels"). זה ממשיך לעבוד, והעורך מציג אותו כאותו אירוע מסומן בכל ערוץ שהוא מכסה.
שינוי מצב הרשמה
לאזור הרשמות יש אפשרות אחת, שינוי מצב הרשמה: כל הצטרפות והסרה בכל ערוץ ורשימה. היא מזרימה את שני אירועי ההסכמה ש-Joryio כבר מתעדת, כך ששום דבר לא נספר פעמיים:
message.subscribed- אדם נרשם לערוץ או לרשימה.statusו-previousStatusמתארים את השינוי,scopeהואchannelאוlist(עםlistId), ו-sourceמציין איך זה קרה (api,dashboard,preference_center,double_opt_in,keyword, הרשמה בחנות או בטופס וכן הלאה).message.unsubscribed- אדם הסיר את עצמו.scopeהואchannel,listאוglobal,reasonמציין איך זה הגיע (link,keyword, ...) ו-unsubscribeSourceמציין את הדרך המדויקת (כפתור ההסרה של אפליקציית הדוא"ל, הכפתור בדף, מרכז ההעדפות, תשובת STOP).
החזרות ודיווחי ספאם הם אירועים נפרדים (message.bounced, message.spamreport). ייבוא אנשי קשר, סנכרון ממחסן נתונים וסנכרון ראשוני של חנות כותבים הסכמה בכמויות ואינם יוצרים את האירועים האלה.
אישורי מסירה
דוחות מסירה, החזרות, דיווחי ספאם, הסרות מרשימה והודעות ש-Joryio לא שלחה הם אישורי מסירה (receipts). בוחרים אותם כמו כל אירוע הודעה אחר, והם מגיעים כאירועים האלה:
| אירוע | ערוצים | מאפיינים בולטים |
|---|---|---|
message.delivered | דוא"ל, SMS, Viber, פוש, WhatsApp | messageId, campaignId, canvasId |
message.bounced | דוא"ל | bounceType (hard או soft), bounceSubType, dsnStatus, reason |
message.spamreport | דוא"ל | messageId, campaignId |
message.failed | כל הערוצים | errorCode, errorReason |
message.skipped | כל הערוצים | reason - למה Joryio לא שלחה (חסימה, מגבלת תדירות, הסכמה, שעות שקטות) |
message.deferred | דוא"ל | הספק מנסה שוב אחרי כשל זמני |
message.auto_replied | דוא"ל | מענה "מחוץ למשרד" - הדוא"ל נמסר |
message.unsubscribed | כל הערוצים | ראו שינוי מצב הרשמה |
message.opened (ערוץ whatsapp) | אישור הקריאה | |
sms.message.received, whatsapp.message.received, viber.message.received | SMS, WhatsApp, Viber | תשובה נכנסת; ב-SMS וב-Viber, keyword הוא stop, start או help כשהתשובה הייתה מילת מפתח |
אישורי מסירה מגיעים רק להזרמת נתונים. הם אף פעם לא מתחילים מסע ולא מפעילים קמפיין, בדיוק כמו קודם.
זרם שנשמר לפני שהיו אישורי מסירה עשוי כבר לבחור חלק מהם (למשל message.bounced). הם מעולם לא הגיעו, והם לא מתחילים להגיע מעצמם: הזרם מציג אישורי מסירה זמינים עכשיו, והם מתחילים לזרום ברגע ששומרים את הזרם שוב. זרמים חדשים מקבלים אישורי מסירה מההתחלה.
מה מכיל אירוע הודעה מוזרם
- כל אירוע נושא את מזהה איש הקשר ב-Joryio (
userId), ואירועי הודעות נושאים גם את ה-externalIdשל איש הקשר כשיש לו. - כתובות דוא"ל ומספרי טלפון נכללים רק כשמי ששמר את הזרם לאחרונה מחזיק בהרשאה
pii:view(למנהלים ולבעלים היא תמיד קיימת). אחרת הם מוסרים. שמירה חוזרת של הזרם מחילה את הכלל לפי מי ששומר. - תוכן ההודעה (גוף, כותרת, HTML) אף פעם לא נכלל באישור מסירה.
- פתיחות והקלקות שנראות אוטומטיות (סורקי אבטחה, שרתי פרטיות של דוא"ל) לא מושמטות; הן נושאות
$is_bot: true, כך שאתם מחליטים מה לעשות בהן.
קבוצות אירועים אחרות
| קבוצה | אירועים |
|---|---|
| In-App | in_app.displayed, in_app.clicked, in_app.dismissed, in_app.converted |
| מסעות | canvas.experiment.entered, canvas.ai_decision.entered, canvas.step.entered, canvas.step.left |
| קמפיינים | campaign.sent, campaign.abtest.winner_declared, campaign.ai_decision.entered |
| כרטיסי Wallet | pass.issued, pass.installed, pass.updated, pass.update_pushed, pass.removed, pass.voided, pass.expired, pass.scanned |
| מסחר אלקטרוני | Order Completed, Order Refunded, Order Cancelled |
| זהות | $identify, $alias |
אפשר גם להזרים כל אירוע מותאם אישית שאתם שולחים עם track().
שליחה באצוות
האירועים נצברים ונמסרים באצוות. אתם מגדירים:
- גודל אצווה - מספר האירועים שנצברים לפני השליחה.
- מרווח ריקון - זמן ההמתנה המרבי לפני מסירת אצווה חלקית.
אצווה נמסרת כשהיא מגיעה לגודל שהוגדר או כשחולף מרווח הריקון - המוקדם מביניהם.
כיצד זה עובד
האירועים נמסרים לזרמים דרך מנגנון הפצה עמיד (durable fan-out), שאינו תלוי באחסון האנליטיקה. כל זרם מקבל את מלוא האירועים מכל הערוצים, ולכן כל יעד ממשיך לקבל אירועים בלי תלות באופן שבו נתוני האנליטיקה נשמרים.
כאשר אירוע תואם לבחירת האירועים של זרם, הוא נכנס לתור ונמסר ליעד באצווה הבאה. כל מסירה כוללת את שם האירוע, המאפיינים, חותמת הזמן ומזהה המשתמש - ובאירועי הודעות גם את ה־channel שממנו נשלחה ההודעה. מחסני נתונים ויעדי אחסון טוענים את האצווה כשורות או כקבצים; יעדי אנליטיקת מוצר מעבירים כל אירוע דרך API הקליטה שלהם; Webhooks מקבלים גוף JSON או NDJSON.
כל זרם אוסף אירועים לאצוות (לפי גודל האצווה ומרווח הריקון) ומנסה למסור אותן שוב במקרה של כשל. אם יעד אינו זמין, אירועים שלא נמסרו מוחזרים לתור ונמסרים מחדש כשהיעד חוזר לפעולה, כך שההשבתה אינה גורמת לאובדן נתונים.
הבטחות מסירה וכפילויות
המסירה היא לפחות פעם אחת (at-least-once). במקרים נדירים - ניסיון חוזר אחרי כשל רשת, או הפעלה מחדש של תהליך באמצע אצווה - אותו אירוע עשוי להימסר ליעד שלכם יותר מפעם אחת.
זו בחירה מכוונת, וזו אותה הבטחה שכל פלטפורמת סטרימינג מספקת. החלופה, מסירה של לכל היותר פעם אחת, הייתה אומרת השמטה שקטה של אירוע כשתוצאת המסירה אינה ידועה; אנחנו מעדיפים לשלוח פעמיים מאשר לאבד.
כל אירוע נושא eventId יציב. זהו אותו ערך בכל מסירה של אותו אירוע, כך שתוכלו להשתמש בו לביטול כפילויות:
| יעד | מה אנחנו עושים |
|---|---|
| Amplitude | נשלח כ-insert_id - Amplitude מבטל כפילויות אוטומטית |
| Mixpanel | נשלח כ-$insert_id - Mixpanel מבטל כפילויות אוטומטית |
| S3 | אצווה שנמסרת שוב נכתבת לאותו מפתח אובייקט, ודורסת במקום להוסיף קובץ נוסף |
| BigQuery, Snowflake, Redshift | בטלו כפילויות לפי eventId בעת הטעינה, למשל ROW_NUMBER() OVER (PARTITION BY eventId) |
| Webhook | התעלמו מ-eventId שכבר עיבדתם |
עבור מחסני נתונים ואחסון, כפילויות אינן מזיקות לאחר ביטול כפילויות בטעינה. עבור webhooks שמבצעים פעולה על כל אירוע - יצירת פנייה, חיוב - בדקו את ה-eventId לפני הפעולה.
ניטור
רשימת הזרמים מציגה מדדים וסטטוס לכל זרם:
- Active - מסירת האירועים פועלת כרגיל
- Paused - הזרם נעצר זמנית (אפשר לחדש אותו בכל עת)
- Error - המסירות האחרונות נכשלו (בדקו את החיבור)
אפשר גם לראות את מספר האירועים שנמסרו ושנכשלו, את שיעור המסירה ואת מועד המסירה האחרונה. פתחו זרם כדי להציג את דף הפרטים שלו, לערוך אותו, להשהות או לחדש אותו, או למחוק אותו.