דלג לתוכן הראשי

Warehouse Syncs API

ייבוא מתוזמן ממחסן הנתונים שלכם - BigQuery,‏ Snowflake או Redshift - אל אותם מסלולי כתיבה שבהם משתמש ייבוא קובץ.

כל נקודות הקצה בעמוד זה יחסיות לכתובת הבסיס: https://api-eu1.joryio.com/api - ראו סקירת API.

סקירה​

סנכרון מחסן קורא טבלה או View שאתם מכינים, וכותב כל עמוד של שורות לתוך Joryio בזמן שהוא מגיע. שום דבר לא נצבר בזיכרון, ולכן סנכרון של עשרה מיליון שורות עולה בזיכרון בדיוק כמו סנכרון של עשרת אלפים.

שני דברים שסנכרון אינו:

  • הוא אינו SQL חופשי. השאילתה נוצרת מחדש בכל ריצה כדי לשאת את תנאי הסינון המצטבר, ולכן אתם נותנים שם של טבלה או View ועמודת watermark, ולא SELECT.
  • הוא אינו מסלול ייבוא נפרד. השורות נכתבות דרך אותו קוד בדיוק שמשמש ייבוא קובץ, כולל כללי המעבר של ההסכמה - כך שביטול הרשמה במחסן שלכם מכובד, ורישום מחדש אינו מתבצע, אלא אם ביקשתם זאת במפורש.

אימות​

Authorization: Bearer YOUR_API_KEY

קריאת סנכרון דורשת integrations:read; יצירה, עדכון, הרצה או מחיקה דורשות integrations:write. הפעלת overrideConsent דורשת בנוסף compliance:write.

אובייקט הסנכרון​

שדהסוגהערות
integrationIduuidחיבור המחסן השמור שדרכו קוראים
connectorIdstringbigquery,‏ snowflake או redshift
syncTypestringusers,‏ events או deletions - קובע לאיזה מסלול כתיבה השורות הולכות
sourceTablestringtable,‏ schema.table או db.schema.table
watermarkColumnstring?העמודה שמעידה ששורה השתנתה. השמטה = רענון מלא בכל ריצה
keyColumnstring?עמודה ייחודית וניתנת למיון לשבירת שוויון. מומלץ - ראו למטה
watermarktimestamp?עד היכן הסנכרון קרא. מנוהל על ידינו; לאיפוס השתמשו ב-Rewind
columnMappingobjectעמודת מחסן אל נתיב שדה יעד
schedulestringmanual,‏ every_5_min,‏ every_15_min,‏ hourly,‏ every_6_hours,‏ daily,‏ weekly,‏ monthly
cadenceobjectלקריאה בלבד. configuredMs,‏ effectiveMs,‏ throttled - ראו למטה
enabledbooleanכבוי = המתזמן מדלג, בלי לאבד את התדירות שנבחרה
overrideConsentbooleanהתייחסות למחסן כמקור אמת להסכמה. דורש compliance:write
lastRunStatsobject?שורות שנקראו, נוצרו, עודכנו, דולגו, נכשלו, משך, ודגימת שגיאות מוגבלת

מיפוי עמודות​

יעדמשמעות
email,‏ phone,‏ externalIdזהות. נדרש לפחות אחד מבין email או externalId בכל שורה
attributes.<key>מאפיין פרופיל. המפתח אינו יכול להכיל נקודה
subscriptions.<channel>הסכמה גלובלית לערוץ - email,‏ sms,‏ whatsapp,‏ push,‏ viber
subscriptions.list.<listId>.<channel>הסכמה בקבוצת מנויים אחת
userId,‏ eventName,‏ timestamp,‏ properties.<key>עבור syncType: events

מחיקות​

סנכרון deletions קורא טבלה של אנשים למחיקה. הוא אינו מוחק בעצמו - הוא יוצר בקשות מחיקה במסלול התאימות הרגיל, אותו מסלול שבו משתמש ה-DSR API. כך המחיקה עוברת על פרופילים, מנויים לרשימות, אירועים, כרטיסי ארנק וקבצים שמורים, מכבדת את הגדרת האנונימיזציה-במקום-מחיקה, ומשאירה את נתיב הביקורת שרגולטור יראה.

  • שורה שמציינת מישהו שמעולם לא יובא, או שכבר נמחק, מדולגת ולא נכשלת.
  • מי שכבר בתהליך מחיקה לא נכנס לתור שוב. השורה נשארת בטבלה שלכם עד שתסירו אותה, כך שבלי ההגנה הזו כל ריצה הייתה מוסיפה בקשת מחיקה נוספת לאותו אדם.

הסכמה​

ההסכמה נעה דרך סנכרון תחת אותם כללים כמו בייבוא קובץ:

  • ביטול הרשמה תמיד מכובד, ללא הרשאה וללא סימון. סנכרון ביטולי הרשמה מה-CRM שלכם אינו דורש דבר.
  • סנכרון לא ירשום מחדש מי שביטל את הרשמתו, ולא יוריד דרגה של הסכמה כפולה מאושרת, אלא אם הוגדר overrideConsent.

ל-overrideConsent יש משמעות גדולה יותר בסנכרון מאשר בקובץ. קובץ מוחל פעם אחת; סנכרון מחיל את המקור שלו מחדש בכל מחזור, כך שעקיפה שנשארה דלוקה מבטלת כל ביטול הרשמה שמגיע בין הריצות, כל עוד הסנכרון קיים.

נקודות קצה​

רשימת סנכרונים​

GET /warehouse-syncs

קבלת סנכרון​

GET /warehouse-syncs/:id

רשימת ריצות​

GET /warehouse-syncs/:id/runs?limit=50

הריצות האחרונות, מהחדשה לישנה. כל אחת נושאת את המונים שלה, את ה-watermark שממנו התחילה ושבו סיימה, דגימה מוגבלת של שגיאות ברמת השורה, ושגיאה ברמת הריצה אם הריצה עצמה נכשלה.

אובייקט הסנכרון אומר אם הוא עובד עכשיו. זה אומר מתי הוא התחיל להיכשל - השאלה האמיתית כשהמספרים נראים שגויים. סנכרון שדילג בשקט על 3% מהשורות במשך חודש נראה, באובייקט הסנכרון, זהה לחלוטין לסנכרון שדילג על 3% הבוקר.

נשמרות 50 הריצות האחרונות לכל סנכרון. דגימת השגיאות מוגבלת משום שמיפוי עמודות שבור מכשיל כל שורה, ומיליון הודעות זהות אינן עוזרות לאיש: המונים הם האמת לגבי היקף, הדגימה היא האמת לגבי הסיבה.

יצירת סנכרון​

POST /warehouse-syncs
{
"integrationId": "0f8e...",
"connectorId": "snowflake",
"name": "Contacts sync",
"syncType": "users",
"sourceTable": "analytics.public.contacts",
"watermarkColumn": "updated_at",
"schedule": "hourly",
"columnMapping": {
"EMAIL": "email",
"CUSTOMER_ID": "externalId",
"PLAN": "attributes.plan",
"EMAIL_CONSENT": "subscriptions.email"
}
}

sourceTable ו-watermarkColumn חייבים להיות מזהים פשוטים. כל דבר אחר - מרכאות, רווחים, נקודה-פסיק, סימני הערה - נדחה עם 400, משום שהשמות האלה משולבים לתוך SQL שנוצר אוטומטית.

עדכון סנכרון​

PATCH /warehouse-syncs/:id

יצירה ועדכון מקבלים רק את השדות שניתן להגדיר: name, integrationId, syncType, sourceTable, sourceQuery, watermarkColumn, keyColumn, columnMapping, schedule, enabled, overrideConsent. כל שדה אחר - id, organizationId, workspaceId, status, watermark או מוני ההרצות - נדחה עם 400; שדות אלה מנוהלים על ידי Joryio. integrationId חייב להיות חיבור מחסן נתונים באותה סביבת עבודה של הסנכרון, אחרת הבקשה מחזירה 404. connectorId הוא אופציונלי ונלקח מהחיבור - אם שולחים אותו, הוא חייב להתאים.

הרצה עכשיו​

POST /warehouse-syncs/:id/run

מחזיר queued: false כאשר הסנכרון כבר בתור או רץ. סנכרון שחופף לעצמו קורא את אותן שורות פעמיים ומכפיל את חשבון המחסן ללא נתונים חדשים.

Rewind (איפוס ה-watermark)​

POST /warehouse-syncs/:id/rewind

מנקה את ה-watermark כך שהריצה הבאה תקרא את הטבלה מההתחלה. הפעולה בטוחה - כל כתיבה היא upsert אידמפוטנטי לפי זהות - וזהו התיקון כשמיפוי עמודות היה שגוי: תקנו את המיפוי, אפסו, הריצו.

מחיקה​

DELETE /warehouse-syncs/:id

קצב אפקטיבי​

ריצה שנמשכת יותר מהמרווח שלה נדחית ולא נכנסת לתור - סנכרון לעולם אינו חופף לעצמו. כך שתזמון של 5 דקות על סנכרון שלוקח 7 דקות רץ למעשה כל 7 דקות, ו- cadence.throttled הוא הדרך לדעת זאת במקום לתהות.

every_5_min הוא הרצפה המעשית לקריאה מתוזמנת, והסיבה היא עלות ולא יכולת: Snowflake מחייב לפי שניות של זמן פעילות מחסן עם מינימום של 60 שניות בכל התעוררות, כך שסנכרון בתדירות כזו לעולם לא נותן למחסן לישון. BigQuery מחייב לפי בייטים שנסרקו, וסינון מצטבר חוסך סריקה מלאה רק כשהטבלה מחולקת (partitioned) או מקובצת (clustered) לפי עמודת ה-watermark.

חודשי​

monthly שומר על היום בחודש שבו הסנכרון התחיל. סנכרון שעוגן ב-31 ירוץ ב-28 בפברואר (29 בשנה מעוברת) ויחזור ל-31 במרץ - הוא לא "יהגר" ל-28 ויישאר שם. כל חישובי התזמון הם ב-UTC, משום שלסנכרון אין אזור זמן, וחישוב בזמן מקומי היה אומר בשקט "אזור הזמן של השרת שבמקרה מריץ את המתזמן".

איך קריאה מצטברת עובדת​

כאשר מוגדרת watermarkColumn, כל ריצה קוראת שורות מה-watermark השמור ואילך, ממוינות לפי אותה עמודה. ה-watermark מתקדם רק אחרי שעמוד נכתב באופן עמיד, כך שריצה שנקטעה קוראת מחדש את הזנב שלה במקום לדלג עליו.

עם keyColumn, בעיית הגבול נעלמת במקום שתנוהל. הסמן הופך ל-(watermarkColumn, keyColumn) וכל ריצה קוראת אך ורק אחרי השורה האחרונה שקראה:

WHERE updated_at > :ts OR (updated_at = :ts AND id > :lastId)
ORDER BY updated_at, id

שורות שחולקות חותמת זמן נקראות בדיוק פעם אחת - לא נאבדות ולא נקראות מחדש - ואין צורך בשאילתת ספירה נוספת. בחרו עמודה מונוטונית: מזהה עולה אוטומטית או ULID. גיבוב תוכן הוא יציב אך בסדר אקראי, ולכן שורה שתתווסף מאוחר יותר באותו רגע תמוין לפני הסמן ולא תיקרא לעולם.

בלי keyColumn אנחנו חוזרים לסמן של חותמת זמן בלבד, והגבול מטופל באחת משתי דרכים:

  • טביעות אצבע (ברירת המחדל, עד 5,000 שורות בגבול). אנחנו זוכרים אילו שורות ישבו ברגע הגבול ומדלגים בדיוק עליהן בריצה הבאה. הגבול נקרא מחדש בכל פעם כדי לזהות אותן, ושום דבר לא מתפספס.
  • ספירת שורות (מעל 5,000). אנחנו זוכרים כמה שורות ישבו שם וקוראים מחדש רק כשהמספר גדל. זול יותר, כי הגבול לא נקרא מחדש, ויש בו חור אחד: שורה שנמחקה ושורה שנוספה באותו רגע משאירות את הספירה ללא שינוי, ולכן השורה החדשה לא נקראת לעולם.

keyColumn חוסכת את שתי הפשרות - שורות הגבול מוחרגות בסעיף ה-WHERE, ולכן הן לא נקראות מחדש ולא מתפספסות. זו ההגדרה המומלצת בדיוק מהסיבה הזו.

הגבול כולל (>=) בכוונה. גבול לא כולל מאבד בשקט שורות שחולקות בדיוק את חותמת הזמן של ה-watermark אך לא נקראו לפני שהריצה הסתיימה. גבול כולל קורא מחדש כמה שורות במקום, וזה לא עולה דבר: כל כתיבה היא upsert אידמפוטנטי, ולכן שורה שמגיעה פעמיים נשמרת פעם אחת.

תצוגה מקדימה של שורות המקור​

POST /warehouse-syncs/preview
{ "integrationId": "0f8e...", "sourceTable": "analytics.public.contacts", "limit": 20 }

קריאה תחומה של המקור כפי שהוא כרגע - בלי סימן מים ובלי תנאי אינקרמנטלי - כדי שתוכלו לבנות מיפוי עמודות מול שמות העמודות האמיתיים ולא מול אלה שאתם זוכרים. שלחו בדיוק אחד מבין sourceTable או sourceQuery. רץ באופן מיידי ומוגבל בתקרה קשיחה של 100 שורות.