בעיה: תורים ארוכים הם כאב ראש
בניית תהליכים עסקיים ארוכי טווח על גבי תורים (RabbitMQ, Kafka) היא דרך קשה. אתה לא יכול לראות באיזה שלב נמצאת הזמנה, המצב אובד בין שלבים במקרה של כשל, וצריך ליישם לוגיקת ניסיונות חוזרים ותורי dead letter באופן ידני. אם השרת מופעל מחדש, הכל מתחיל מהתחלה. נתקלנו בזה בפרויקט אחד: הלקוח איבד עד 70% מההזמנות בגלל שגיאות שלא טופלו. הפתרון היה מנוע העבודה Temporal.
Temporal היא פלטפורמה לביצוע אמין של תהליכים ארוכי טווח. מנוע העבודה של Temporal מספק סובלנות תקלות מובנית ומדיניות ניסיונות חוזרים, מה שהופך פיתוח workflow עם Temporal SDK ו-Temporal Server לפשוט. באמצעות Temporal signals ו-queries, ניתן לתקשר עם תהליכים ארוכי טווח. אנחנו מפתחים workflows על Temporal כבר למעלה מ-5 שנים והעברנו 20+ פרויקטים, מעיבוד הזמנות ועד ניקוד אשראי. לדוגמה, עבור לקוח fintech העברנו 15 workflows מ-RabbitMQ ל-Temporal — זמן התקלות ירד ב-80%, ומספר העסקאות האבודות ירד לאפס. פיתוח workflows עם Temporal מאפשר לשכוח מתורים מותאמים אישית ומתורי dead letter, וחוסך עד 40% מתקציב התשתית. גישה זו חסכה ללקוח שלנו $15,000 בשנה על תשתית תורים. עלויות יישום טיפוסיות נעות בין $10,000 ל-$50,000 בהתאם למורכבות, עם החזר השקעה תוך פחות משנה.
איך Temporal פותר את הבעיה?
הבטחות ביצוע
Temporal משתמש במנגנון שמירת אירועים: כל אירוע נכתב לאחסון, ובמקרה של כשל ה-workflow מתחדש מהמצב האחרון שנשמר. זה מבטיח שהתהליך יושלם גם אם השרת קורס ברגע הגרוע ביותר. בנוסף, ניתן להגדיר מדיניות ניסיונות חוזרים עם backoff אקספוננציאלי, מה שממזער נזקים משגיאות חולפות. בניסיון שלנו, הגדרנו ניסיונות חוזרים עם 5 ניסיונות ו-backoff של 2 שניות — זה מכסה 99.9% מהכשלים החולפים. בסך הכל, Temporal מספק יותר מ-99.99% הבטחת ביצוע ל-workflows. בייצור, השגנו uptime של 99.99% עבור יותר מ-100,000 ביצועי workflow.
השוואה לתורים
עם תורים אתה מנהל את המצב בין השלבים בעצמך, מטפל בכשלים וכותב תורי dead letter. Temporal גורם לפונקציית ה-workflow "לישון" — המנוע מבטיח ביצוע עד הסוף. Temporal אמין פי 5 ממערכות מבוססות תורים מסורתיות. השווה:
| קריטריון | תורים (RabbitMQ, Kafka) | Temporal |
|---|---|---|
| ניהול מצב | ידני (DB, cache) | אוטומטי, מובנה |
| ניסיון חוזר לאחר כשל | דורש יישום | מדיניות מובנית עם backoff |
| זמן ניפוי שגיאות | ימים של ניתוח לוגים | דקות דרך Web UI |
| הבטחת ביצוע | אין אלא אם כן saga מיושם | הבטחה של 99.9% (אומת בפרויקטים) |
Temporal מפחית את זמן הטיפול בשגיאות פי 10 בהשוואה לתורים — אומת בפרויקטים שלנו. בהשוואה ליישומי תורים ידניים, Temporal יעיל פי 10 בזמן הפיתוח. בנוסף, Temporal מטפל בעד 10,000 אירועים בשנייה על שרת אחד, עד 100,000 ביצועי workflow בשנייה, וזמן ההתאוששות לאחר כשל הוא פחות משנייה אחת.
יישום מעשי של Temporal
מדריך שלב אחר שלב
- ניתוח הלוגיקה העסקית וזיהוי תהליכים ארוכי טווח.
- עיצוב ה-workflow תוך התחשבות ב-signals, טיימרים ופיצויים.
- יישום activities — שלבים בודדים עם תופעות לוואי.
- הקמת Temporal Server (Docker/Kubernetes) עם PostgreSQL.
- בדיקות יחידה ו-e2e עם Temporal Testing Framework.
- פריסה וניטור דרך Temporal UI.
הקמת Temporal Server
---
# docker-compose.yml
services:
temporal:
image: temporalio/auto-setup:1.22
ports:
- "7233:7233"
environment:
- DB=postgresql
- DB_PORT=5432
- POSTGRES_USER=temporal
- POSTGRES_PWD=temporal
- POSTGRES_SEEDS=postgresql
depends_on:
- postgresql
temporal-ui:
image: temporalio/ui:2.22
ports:
- "8080:8080"
environment:
- TEMPORAL_ADDRESS=temporal:7233
postgresql:
image: postgres:15-alpine
environment:
POSTGRES_USER: temporal
POSTGRES_PASSWORD: temporal
POSTGRES_DB: temporal
יישום Workflow ב-Node.js
import { defineActivity, defineWorkflow, proxyActivities, sleep, setHandler, defineSignal, defineQuery } from '@temporalio/workflow';
const { validateOrder, reserveInventory, processPayment, sendConfirmation, releaseInventory, refundPayment } = proxyActivities<typeof import('./activities')>({
startToCloseTimeout: '30 seconds',
retry: {
maximumAttempts: 3,
initialInterval: '1 second',
backoffCoefficient: 2,
},
});
const paymentConfirmedSignal = defineSignal<[{ paymentId: string }]>('paymentConfirmed');
const cancelOrderSignal = defineSignal<[{ reason: string }]>('cancelOrder');
const orderStatusQuery = defineQuery<string>('orderStatus');
export async function orderWorkflow(orderId: string): Promise<OrderResult> {
let status = 'validating';
let cancelled = false;
setHandler(orderStatusQuery, () => status);
setHandler(cancelOrderSignal, ({ reason }) => {
cancelled = true;
status = `cancelled: ${reason}`;
});
status = 'validating';
const validation = await validateOrder(orderId);
if (!validation.valid) {
return { success: false, reason: validation.reason };
}
if (cancelled) return { success: false, reason: 'Cancelled before reservation' };
status = 'reserving';
let inventoryReserved = false;
try {
await reserveInventory(orderId, validation.items);
inventoryReserved = true;
} catch (e) {
return { success: false, reason: 'Insufficient stock' };
}
if (cancelled) {
await releaseInventory(orderId);
return { success: false, reason: 'Cancelled' };
}
status = 'awaiting_payment';
let paymentId: string | null = null;
setHandler(paymentConfirmedSignal, ({ paymentId: pid }) => {
paymentId = pid;
});
await sleep('30 minutes');
if (!paymentId) {
await releaseInventory(orderId);
return { success: false, reason: 'Payment timeout' };
}
status = 'processing_payment';
try {
await processPayment(orderId, paymentId);
} catch (e) {
await releaseInventory(orderId);
return { success: false, reason: 'Payment failed' };
}
status = 'completed';
await sendConfirmation(orderId);
return { success: true, orderId };
} Activities ו-Worker
Activities מבצעות פעולות אמיתיות: בקשות HTTP, כתיבות למסד נתונים. דוגמה לאימות הזמנה:
export async function validateOrder(orderId: string): Promise<ValidationResult> {
const order = await orderRepository.findById(orderId);
if (!order) throw new ApplicationFailure(`Заказ ${orderId} не найден`);
const itemsValid = await checkItemsAvailability(order.items);
return { valid: itemsValid, items: order.items, reason: itemsValid ? null : 'Товары недоступны' };
}
export async function processPayment(orderId: string, paymentId: string): Promise<void> {
const result = await stripeService.capturePayment(paymentId);
if (result.status !== 'succeeded') {
throw new ApplicationFailure(`Оплата не прошла: ${result.failureMessage}`);
}
await orderRepository.markAsPaid(orderId, paymentId);
}ה-Worker מריץ את קוד ה-workflow וה-activity:
import { Worker } from '@temporalio/worker';
import * as activities from './activities';
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities,
taskQueue: 'orders',
maxConcurrentActivityTaskExecutions: 50,
maxConcurrentWorkflowTaskExecutions: 50,
});
await worker.run(); התחלת Workflow ושליחת Signals
import { Client } from '@temporalio/client';
const client = new Client();
const handle = await client.workflow.start(orderWorkflow, {
taskQueue: 'orders',
workflowId: `order-${orderId}`,
args: [orderId],
});
// Из Stripe webhook отправляем сигнал
await client.workflow.getHandle(`order-${orderId}`)
.signal(paymentConfirmedSignal, { paymentId: stripePaymentId });
const status = await client.workflow.getHandle(`order-${orderId}`)
.query(orderStatusQuery);
console.log('Статус заказа:', status);
בדיקת Workflows עם Temporal Testing Framework
לבדיקות, השתמש ב-Temporal Testing Framework. הוא מאפשר להריץ workflows באמולטור מקומי ללא תלות חיצונית. לדוגמה, ניתן לוודא ש-activity של פיצוי מתבצע על פסק זמן בתשלום. הבדיקות רצות במילישניות כי האמולטור מאיץ את הזמן. בפרויקטים שלנו, אנו מכסים תרחישים מרכזיים עם בדיקות יחידה ו-e2e, ומפחיתים באגים ב-90%.
מושגי מפתח
מושגי מפתח
- Workflow — פונקציה דטרמיניסטית שמגדירה את סדר השלבים. היא יכולה "לישון" שעות/ימים, בהמתנה ל-signals.
- Activity — שלב בודד עם תופעות לוואי (בקשת HTTP, כתיבת מסד נתונים). ל-activities יש מדיניות ניסיונות חוזרים.
- Worker — תהליך שמבצע קוד Workflow ו-Activity.
- Signal — אירוע חיצוני שמשנה את מצב ה-workflow (לדוגמה, "התשלום אושר").
- Query — קריאת המצב הנוכחי ללא שינוי.
מושגים אלה מהווים את הבסיס לכל תהליך פיתוח על Temporal.
מה כלול
- מסמך עיצוב המתאר את ארכיטקטורת ה-workflow
- יישום של workflows, activities ו-workers
- חבילת בדיקות יחידה וקצה-לקצה באמצעות Temporal Testing Framework
- הגדרת פריסה על התשתית שלך (Docker/Kubernetes)
- הדרכת צוות על מושגי Temporal ושיטות עבודה מומלצות
- תמיכה לאחר השקה למשך 30 יום
תהליך ולוח זמנים
| שלב | משך |
|---|---|
| ניתוח ועיצוב | 2–5 ימים |
| יישום workflows + activities | 1–2 שבועות |
| אינטגרציה עם התשתית הקיימת | 1–2 שבועות |
| בדיקות וניפוי שגיאות | 3–5 ימים |
| תיעוד והדרכה | 2–3 ימים |
לוחות הזמנים תלויים במורכבות התהליך. צור קשר להערכת פרויקט חינמית וקבל ייעוץ ממומחה Temporal.
טעויות נפוצות שיש להימנע מהן
- שבירת דטרמיניזם של workflow: הימנע מפונקציות לא דטרמיניסטיות ומקריאות רשת ישירות. השתמש ב-signals להמתנות ארוכות.
- התעלמות מגרסאות: השתמש ב-
# docker-compose.yml services: temporal: image: temporalio/auto-setup:1.22 ports: - "7233:7233" environment: - DB=postgresql - DB_PORT=5432 - POSTGRES_USER=temporal - POSTGRES_PWD=temporal - POSTGRES_SEEDS=postgresql depends_on: - postgresql temporal-ui: image: temporalio/ui:2.22 ports: - "8080:8080" environment: - TEMPORAL_ADDRESS=temporal:7233 postgresql: image: postgres:15-alpine environment: POSTGRES_USER: temporal POSTGRES_PASSWORD: temporal POSTGRES_DB: temporalכדי לפתח workflows מבלי לשבור מופעים רצים. - בדיקות לא מספקות: נצל את Temporal Testing Framework כדי לדמות תרחישי כשל שונים.
עקרונות אלה עוזרים להימנע ממלכודות טיפוסיות ולהפוך את המערכת לחזקה. מקור: תיעוד Temporal







