5 techniques JavaScript pour la récupération des erreurs de workflow
Explorez les techniques JavaScript essentielles pour une récupération efficace des erreurs de flux de travail, garantissant la stabilité et la résilience des processus d'automatisation.

La récupération des erreurs est l’épine dorsale de tout flux de travail automatisé fiable. JavaScript, grâce à sa nature asynchrone et événementielle, offre des outils puissants pour garantir que les workflows peuvent gérer les perturbations telles que les dépassements de délai d'API ou les incohérences de données. En mettant en œuvre des techniques telles que blocs try-catch, classes d'erreur personnalisées et mécanismes de nouvelle tentative, vous pouvez protéger vos processus contre les pannes et maintenir la stabilité du système. Des plateformes telles que Laténode rendez cela encore plus facile, en fournissant plus de 300 intégrations et des capacités de script personnalisées pour créer des environnements résilients workflows d'automatisation époustouflant adapté à vos besoins.
Décomposons cinq techniques essentielles pour la récupération des erreurs dans les workflows JavaScript et comment vous pouvez les appliquer efficacement.
JavaScript Conseils et astuces pour la gestion et le suivi des erreurs
1. Try-Catch et gestion des erreurs asynchrones
L'espace essayer-attraper Le bloc agit comme votre principale protection contre les perturbations du flux de travail, en capturant les erreurs avant qu'elles ne se propagent et ne provoquent des problèmes généralisés dans votre automatisation. Si la structure try-catch de base fonctionne bien pour le code synchrone, les flux de travail asynchrones nécessitent une approche plus personnalisée.
Pour opérations synchronesLe bloc try-catch standard est simple et efficace. Cependant, les automatisations impliquant des appels d'API, des requêtes de base de données ou la gestion de fichiers reposent souvent sur des workflows asynchrones. Dans ce cas, les rejets de promesses non gérés peuvent mettre fin de manière inattendue à l'ensemble du processus, ce qui rend essentielle une gestion rigoureuse des erreurs.
<span class="hljs-comment">// Basic synchronous error handling</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">processWorkflowData</span>(<span class="hljs-params">data</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> result = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(data);
<span class="hljs-keyword">return</span> <span class="hljs-title function_">validateBusinessRules</span>(result);
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Data processing failed:'</span>, error.<span class="hljs-property">message</span>);
<span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'Invalid data format'</span> };
}
}
Pour flux de travail asynchronesgrâce à un protocole async/await fournit une manière plus claire et plus lisible de gérer efficacement les promesses.
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">executeWorkflowStep</span>(<span class="hljs-params">apiEndpoint, payload</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(apiEndpoint, {
<span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
<span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
<span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(payload)
});
<span class="hljs-keyword">if</span> (!response.<span class="hljs-property">ok</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`API call failed: <span class="hljs-subst">${response.status}</span>`</span>);
}
<span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>();
<span class="hljs-keyword">return</span> { <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, data };
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>()
};
}
}
Alternativement, Chaînes Promise.catch() peut être utilisé pour gérer les erreurs dans les flux de travail qui reposent fortement sur des promesses chaînées.
<span class="hljs-keyword">function</span> <span class="hljs-title function_">processWorkflowChain</span>(<span class="hljs-params">inputData</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-title function_">validateInput</span>(inputData)
.<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">data</span> =></span> <span class="hljs-title function_">transformData</span>(data))
.<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">transformed</span> =></span> <span class="hljs-title function_">saveToDatabase</span>(transformed))
.<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">saved</span> =></span> <span class="hljs-title function_">notifyCompletion</span>(saved))
.<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">error</span> =></span> {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Workflow chain failed:'</span>, error);
<span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: <span class="hljs-string">'failed'</span>, <span class="hljs-attr">step</span>: error.<span class="hljs-property">step</span> || <span class="hljs-string">'unknown'</span> };
});
}
Lorsque vous travaillez avec Flux de travail LatenodeCes techniques de gestion des erreurs peuvent être intégrées à des nœuds JavaScript personnalisés. Cela permet d'isoler les défaillances et de garantir la stabilité de vos automatisations, même lors de la connexion de plusieurs services. En enveloppant les appels d'API et les transformations de données dans des blocs try-catch, vous pouvez empêcher les défaillances ponctuelles de perturber l'ensemble du flux de travail. Ceci est particulièrement utile pour gérer des intégrations complexes au sein de la vaste bibliothèque de plus de 300 services de Latenode, où des problèmes réseau ou des interruptions temporaires de service pourraient compromettre votre automatisation.
Pour ajouter une couche supplémentaire de résilience, gestionnaires d'erreurs globaux peuvent intercepter les erreurs qui échappent aux blocs try-catch locaux. Ces gestionnaires garantissent la journalisation des échecs inattendus et peuvent déclencher des mécanismes de récupération.
<span class="hljs-comment">// Global unhandled promise rejection handler</span>
process.<span class="hljs-title function_">on</span>(<span class="hljs-string">'unhandledRejection'</span>, <span class="hljs-function">(<span class="hljs-params">reason, promise</span>) =></span> {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Unhandled Promise Rejection:'</span>, reason);
<span class="hljs-comment">// Log error or trigger alert</span>
<span class="hljs-title function_">logErrorToMonitoring</span>({
<span class="hljs-attr">type</span>: <span class="hljs-string">'unhandledRejection'</span>,
<span class="hljs-attr">reason</span>: reason.<span class="hljs-title function_">toString</span>(),
<span class="hljs-attr">timestamp</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>()
});
});
Pour optimiser les stratégies de récupération, concentrez-vous sur la capture des erreurs au niveau d'opérations spécifiques plutôt que de fonctions entières. Cette approche ciblée vous permet de mettre en œuvre des plans de récupération adaptés à la nature et à la localisation de chaque erreur. Nous verrons ensuite comment les classes d'erreurs personnalisées peuvent améliorer ce processus en fournissant davantage de contexte pour la gestion des erreurs.
2. Classes d'erreur personnalisées pour la récupération contextuelle
Les classes d'erreur personnalisées apportent de la clarté à la gestion des erreurs en transformant les erreurs génériques en objets riches en contexteCela permet aux workflows de réagir intelligemment en fonction du type de défaillance spécifique. Alors que les erreurs JavaScript standard offrent des détails limités, les classes d'erreurs personnalisées catégorisent les problèmes, facilitant ainsi l'application de stratégies de récupération ciblées.
<span class="hljs-comment">// Base custom error class</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">WorkflowError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">Error</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">message, code, recoverable = <span class="hljs-literal">true</span></span>) {
<span class="hljs-variable language_">super</span>(message);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">name</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">constructor</span>.<span class="hljs-property">name</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">code</span> = code;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">recoverable</span> = recoverable;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">timestamp</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>();
<span class="hljs-variable language_">this</span>.<span class="hljs-property">context</span> = {};
}
<span class="hljs-title function_">addContext</span>(<span class="hljs-params">key, value</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">context</span>[key] = value;
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>;
}
}
<span class="hljs-comment">// Specific error types for different failure scenarios</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">NetworkError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">WorkflowError</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">message, statusCode, endpoint</span>) {
<span class="hljs-variable language_">super</span>(message, <span class="hljs-string">'NETWORK_ERROR'</span>, <span class="hljs-literal">true</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">statusCode</span> = statusCode;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">endpoint</span> = endpoint;
}
}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">ValidationError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">WorkflowError</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">message, field, value</span>) {
<span class="hljs-variable language_">super</span>(message, <span class="hljs-string">'VALIDATION_ERROR'</span>, <span class="hljs-literal">false</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">field</span> = field;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">invalidValue</span> = value;
}
}
<span class="hljs-keyword">class</span> <span class="hljs-title class_">RateLimitError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">WorkflowError</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">message, retryAfter</span>) {
<span class="hljs-variable language_">super</span>(message, <span class="hljs-string">'RATE_LIMIT'</span>, <span class="hljs-literal">true</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">retryAfter</span> = retryAfter;
}
}
Grâce à ces classes personnalisées, les workflows peuvent identifier les types d'erreurs et appliquer des méthodes de récupération personnalisées. Par exemple, les erreurs réseau peuvent déclencher une nouvelle tentative, les erreurs de validation peuvent entraîner une correction des données et les erreurs de limite de débit peuvent retarder intelligemment les requêtes ultérieures.
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">executeWorkflowWithRecovery</span>(<span class="hljs-params">operation, data</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>(data);
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-comment">// Handle different error types with specific recovery strategies</span>
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">NetworkError</span>) {
<span class="hljs-keyword">if</span> (error.<span class="hljs-property">statusCode</span> >= <span class="hljs-number">500</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Server error detected, retrying in 5 seconds...`</span>);
<span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">5000</span>));
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>(data); <span class="hljs-comment">// Retry once for server errors</span>
}
<span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Client errors (4xx) are not retryable</span>
}
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">RateLimitError</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Rate limited, waiting <span class="hljs-subst">${error.retryAfter}</span> seconds`</span>);
<span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, error.<span class="hljs-property">retryAfter</span> * <span class="hljs-number">1000</span>));
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>(data);
}
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">ValidationError</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">`Data validation failed for field: <span class="hljs-subst">${error.field}</span>`</span>);
<span class="hljs-comment">// Log for manual review, don't retry</span>
<span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: <span class="hljs-string">'failed'</span>, <span class="hljs-attr">reason</span>: <span class="hljs-string">'invalid_data'</span>, <span class="hljs-attr">field</span>: error.<span class="hljs-property">field</span> };
}
<span class="hljs-comment">// Unknown error type - handle generically</span>
<span class="hljs-keyword">throw</span> error;
}
}
In Intégrations API, les erreurs personnalisées aident à normaliser diverses réponses dans des formats clairs et exploitables.
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">callExternalAPI</span>(<span class="hljs-params">endpoint, payload</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(endpoint, {
<span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
<span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> },
<span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(payload)
});
<span class="hljs-keyword">if</span> (response.<span class="hljs-property">status</span> === <span class="hljs-number">429</span>) {
<span class="hljs-keyword">const</span> retryAfter = <span class="hljs-built_in">parseInt</span>(response.<span class="hljs-property">headers</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">'Retry-After'</span>)) || <span class="hljs-number">60</span>;
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RateLimitError</span>(<span class="hljs-string">'API rate limit exceeded'</span>, retryAfter);
}
<span class="hljs-keyword">if</span> (response.<span class="hljs-property">status</span> >= <span class="hljs-number">500</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">NetworkError</span>(
<span class="hljs-string">`Server error: <span class="hljs-subst">${response.statusText}</span>`</span>,
response.<span class="hljs-property">status</span>,
endpoint
);
}
<span class="hljs-keyword">if</span> (!response.<span class="hljs-property">ok</span>) {
<span class="hljs-keyword">const</span> errorData = <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>();
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ValidationError</span>(
errorData.<span class="hljs-property">message</span> || <span class="hljs-string">'Request validation failed'</span>,
errorData.<span class="hljs-property">field</span>,
errorData.<span class="hljs-property">value</span>
);
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> response.<span class="hljs-title function_">json</span>();
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">WorkflowError</span>) {
<span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Re-throw custom errors as-is</span>
}
<span class="hljs-comment">// Convert generic errors to custom format</span>
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">NetworkError</span>(
<span class="hljs-string">`Network request failed: <span class="hljs-subst">${error.message}</span>`</span>,
<span class="hljs-number">0</span>,
endpoint
);
}
}
Lorsque vous travaillez avec LaténodeLes classes d'erreur personnalisées sont particulièrement utiles pour gérer des workflows complexes impliquant plusieurs services. Par exemple, vous pouvez définir des types d'erreur spécifiques pour les problèmes de connexion à la base de données, d'authentification ou de transformation de données. Chaque type d'erreur peut avoir sa propre logique de récupération, garantissant ainsi une exécution fluide du workflow.
<span class="hljs-comment">// Latenode-specific error handling for multi-service workflows</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">IntegrationError</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">WorkflowError</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">message, service, operation</span>) {
<span class="hljs-variable language_">super</span>(message, <span class="hljs-string">'INTEGRATION_ERROR'</span>, <span class="hljs-literal">true</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">service</span> = service;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">operation</span> = operation;
}
}
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">processLatenodeWorkflow</span>(<span class="hljs-params">data</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// Step 1: Validate incoming data</span>
<span class="hljs-keyword">const</span> validated = <span class="hljs-title function_">validateWorkflowData</span>(data);
<span class="hljs-comment">// Step 2: Process through multiple services</span>
<span class="hljs-keyword">const</span> processed = <span class="hljs-keyword">await</span> <span class="hljs-title function_">callExternalAPI</span>(<span class="hljs-string">'/api/transform'</span>, validated);
<span class="hljs-comment">// Step 3: Store results</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">saveToDatabase</span>(processed);
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">ValidationError</span>) {
<span class="hljs-comment">// Send to error queue for manual review</span>
<span class="hljs-keyword">await</span> <span class="hljs-title function_">logToErrorQueue</span>({
<span class="hljs-attr">type</span>: <span class="hljs-string">'validation_failed'</span>,
<span class="hljs-attr">data</span>: data,
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">field</span>: error.<span class="hljs-property">field</span>
});
<span class="hljs-keyword">return</span> { <span class="hljs-attr">status</span>: <span class="hljs-string">'queued_for_review'</span> };
}
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">IntegrationError</span>) {
<span class="hljs-comment">// Attempt alternative service or fallback</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`<span class="hljs-subst">${error.service}</span> failed, trying fallback method`</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">executeWorkflowFallback</span>(data);
}
<span class="hljs-keyword">throw</span> error; <span class="hljs-comment">// Unhandled error types bubble up</span>
}
}
Les classes d’erreurs personnalisées améliorent également la journalisation des erreurs, ce qui facilite le suivi et la résolution des problèmes.
<span class="hljs-comment">// Enhanced error logging with custom classes</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">logWorkflowError</span>(<span class="hljs-params">error</span>) {
<span class="hljs-keyword">if</span> (error <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">WorkflowError</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Workflow Error Details:'</span>, {
<span class="hljs-attr">type</span>: error.<span class="hljs-property">name</span>,
<span class="hljs-attr">code</span>: error.<span class="hljs-property">code</span>,
<span class="hljs-attr">message</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">recoverable</span>: error.<span class="hljs-property">recoverable</span>,
<span class="hljs-attr">timestamp</span>: error.<span class="hljs-property">timestamp</span>,
<span class="hljs-attr">context</span>: error.<span class="hljs-property">context</span>
});
} <span class="hljs-keyword">else</span> {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Unexpected Error:'</span>, error);
}
}
3. Relancement des erreurs et enrichissement du contexte
La relance d'erreur est une technique qui optimise la gestion des erreurs en préservant l'erreur d'origine tout en ajoutant un contexte pertinent. Cette approche garantit que l'intégralité de la trace de l'erreur reste intacte, ce qui facilite l'identification de la cause première et inclut des détails spécifiques au workflow qui facilitent le débogage et la récupération.
Fondamentalement, cette méthode consiste à détecter les erreurs à différents niveaux du workflow, à les enrichir de contexte supplémentaire et à les relancer. Le résultat est une chaîne d'erreurs détaillée qui met en évidence non seulement la cause du problème, mais aussi où, quand et dans quelles conditions il s'est produit.
<span class="hljs-comment">// Context enrichment wrapper function</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">enrichErrorContext</span>(<span class="hljs-params">operation, context</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>();
} <span class="hljs-keyword">catch</span> (originalError) {
<span class="hljs-comment">// Create an enriched error with added context</span>
<span class="hljs-keyword">const</span> enrichedError = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`<span class="hljs-subst">${context.operation}</span> failed: <span class="hljs-subst">${originalError.message}</span>`</span>);
enrichedError.<span class="hljs-property">originalError</span> = originalError;
enrichedError.<span class="hljs-property">context</span> = {
<span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>(),
<span class="hljs-attr">operation</span>: context.<span class="hljs-property">operation</span>,
<span class="hljs-attr">step</span>: context.<span class="hljs-property">step</span>,
<span class="hljs-attr">data</span>: context.<span class="hljs-property">data</span>,
<span class="hljs-attr">environment</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">NODE_ENV</span> || <span class="hljs-string">'development'</span>
};
<span class="hljs-comment">// Append the original stack trace for full transparency</span>
enrichedError.<span class="hljs-property">stack</span> = <span class="hljs-string">`<span class="hljs-subst">${enrichedError.stack}</span>Caused by: <span class="hljs-subst">${originalError.stack}</span>`</span>;
<span class="hljs-keyword">throw</span> enrichedError;
}
}
Cette technique s’appuie sur les pratiques standard de gestion des erreurs en intégrant des détails exploitables à chaque étape du flux de travail.
Exemple de flux de travail multicouche
Considérez un flux de travail multicouche où les erreurs sont enrichies à chaque étape pour capturer des informations détaillées :
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">processDataWorkflow</span>(<span class="hljs-params">inputData</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// Layer 1: Data validation</span>
<span class="hljs-keyword">const</span> validatedData = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">validateInputData</span>(inputData),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'data_validation'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-number">1</span>,
<span class="hljs-attr">data</span>: { <span class="hljs-attr">recordCount</span>: inputData.<span class="hljs-property">length</span>, <span class="hljs-attr">source</span>: inputData.<span class="hljs-property">source</span> }
}
);
<span class="hljs-comment">// Layer 2: Data transformation</span>
<span class="hljs-keyword">const</span> transformedData = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">transformData</span>(validatedData),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'data_transformation'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-number">2</span>,
<span class="hljs-attr">data</span>: { <span class="hljs-attr">inputSize</span>: validatedData.<span class="hljs-property">length</span>, <span class="hljs-attr">transformType</span>: <span class="hljs-string">'normalize'</span> }
}
);
<span class="hljs-comment">// Layer 3: External API call</span>
<span class="hljs-keyword">const</span> apiResult = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">callExternalService</span>(transformedData),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'external_api_call'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-number">3</span>,
<span class="hljs-attr">data</span>: { <span class="hljs-attr">endpoint</span>: <span class="hljs-string">'/api/process'</span>, <span class="hljs-attr">payloadSize</span>: transformedData.<span class="hljs-property">length</span> }
}
);
<span class="hljs-keyword">return</span> apiResult;
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-comment">// Add workflow-level context</span>
error.<span class="hljs-property">workflowId</span> = <span class="hljs-title function_">generateWorkflowId</span>();
error.<span class="hljs-property">totalSteps</span> = <span class="hljs-number">3</span>;
error.<span class="hljs-property">failureRate</span> = <span class="hljs-keyword">await</span> <span class="hljs-title function_">calculateRecentFailureRate</span>();
<span class="hljs-keyword">throw</span> error;
}
}
Opérations imbriquées et enrichissement du contexte
Pour les workflows comportant des opérations imbriquées, l'enrichissement du contexte est encore plus performant. Il permet un suivi détaillé des erreurs à plusieurs niveaux. Par exemple, dans les opérations de base de données, les erreurs peuvent être capturées et enrichies comme suit :
<span class="hljs-keyword">class</span> <span class="hljs-title class_">DatabaseManager</span> {
<span class="hljs-keyword">async</span> <span class="hljs-title function_">executeQuery</span>(<span class="hljs-params">query, params, context = {}</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span>.<span class="hljs-title function_">query</span>(query, params);
} <span class="hljs-keyword">catch</span> (dbError) {
<span class="hljs-keyword">const</span> enrichedError = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Database query failed: <span class="hljs-subst">${dbError.message}</span>`</span>);
enrichedError.<span class="hljs-property">originalError</span> = dbError;
enrichedError.<span class="hljs-property">queryContext</span> = {
<span class="hljs-attr">query</span>: query.<span class="hljs-title function_">substring</span>(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">'...'</span>, <span class="hljs-comment">// Truncated for logging</span>
<span class="hljs-attr">paramCount</span>: params ? params.<span class="hljs-property">length</span> : <span class="hljs-number">0</span>,
<span class="hljs-attr">connection</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span>.<span class="hljs-property">threadId</span>,
<span class="hljs-attr">database</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">connection</span>.<span class="hljs-property">config</span>.<span class="hljs-property">database</span>,
...context
};
<span class="hljs-keyword">throw</span> enrichedError;
}
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">getUserData</span>(<span class="hljs-params">userId, includeHistory = <span class="hljs-literal">false</span></span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> query = includeHistory
? <span class="hljs-string">'SELECT * FROM users u LEFT JOIN user_history h ON u.id = h.user_id WHERE u.id = ?'</span>
: <span class="hljs-string">'SELECT * FROM users WHERE id = ?'</span>;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">executeQuery</span>(query, [userId], {
<span class="hljs-attr">operation</span>: <span class="hljs-string">'get_user_data'</span>,
<span class="hljs-attr">userId</span>: userId,
<span class="hljs-attr">includeHistory</span>: includeHistory,
<span class="hljs-attr">queryType</span>: <span class="hljs-string">'SELECT'</span>
});
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-comment">// Append user-specific context</span>
error.<span class="hljs-property">userContext</span> = {
<span class="hljs-attr">requestedUserId</span>: userId,
<span class="hljs-attr">includeHistory</span>: includeHistory,
<span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>()
};
<span class="hljs-keyword">throw</span> error;
}
}
}
Application de l'enrichissement du contexte dans Laténode Les flux de travail
Lors de l'intégration de plusieurs services dans Laténode Dans les workflows, l'enrichissement du contexte offre une vision claire des erreurs et des données traitées. Voici un exemple :
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">executeLatenodeIntegration</span>(<span class="hljs-params">workflowData</span>) {
<span class="hljs-keyword">const</span> workflowId = <span class="hljs-string">`workflow_<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>`</span>;
<span class="hljs-keyword">const</span> startTime = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// Step 1: Fetch data from CRM</span>
<span class="hljs-keyword">const</span> crmData = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">fetchFromCRM</span>(workflowData.<span class="hljs-property">crmId</span>),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'crm_data_fetch'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-string">'crm_integration'</span>,
<span class="hljs-attr">workflowId</span>: workflowId,
<span class="hljs-attr">service</span>: <span class="hljs-string">'salesforce'</span>
}
);
<span class="hljs-comment">// Step 2: Process with AI</span>
<span class="hljs-keyword">const</span> processedData = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">processWithAI</span>(crmData),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'ai_processing'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-string">'ai_analysis'</span>,
<span class="hljs-attr">workflowId</span>: workflowId,
<span class="hljs-attr">service</span>: <span class="hljs-string">'openai_gpt4'</span>,
<span class="hljs-attr">inputTokens</span>: <span class="hljs-title function_">estimateTokens</span>(crmData)
}
);
<span class="hljs-comment">// Step 3: Update database</span>
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-title function_">enrichErrorContext</span>(
<span class="hljs-function">() =></span> <span class="hljs-title function_">updateDatabase</span>(processedData),
{
<span class="hljs-attr">operation</span>: <span class="hljs-string">'database_update'</span>,
<span class="hljs-attr">step</span>: <span class="hljs-string">'data_persistence'</span>,
<span class="hljs-attr">workflowId</span>: workflowId,
<span class="hljs-attr">recordCount</span>: processedData.<span class="hljs-property">length</span>
}
);
<span class="hljs-keyword">return</span> result;
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-comment">// Add overall workflow metadata</span>
error.<span class="hljs-property">workflowMetadata</span> = {
<span class="hljs-attr">workflowId</span>: workflowId,
<span class="hljs-attr">totalExecutionTime</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>() - startTime,
<span class="hljs-attr">originalInput</span>: workflowData,
<span class="hljs-attr">failurePoint</span>: error.<span class="hljs-property">context</span>?.<span class="hljs-property">step</span> || <span class="hljs-string">'unknown'</span>,
<span class="hljs-attr">retryable</span>: <span class="hljs-title function_">determineIfRetryable</span>(error)
};
<span class="hljs-comment">// Log enriched error details</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Workflow failed with enriched context:'</span>, {
<span class="hljs-attr">message</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">context</span>: error.<span class="hljs-property">context</span>,
<span class="hljs-attr">workflowMetadata</span>: error.<span class="hljs-property">workflowMetadata</span>,
<span class="hljs-attr">originalError</span>: error.<span class="hljs-property">originalError</span>?.<span class="hljs-property">message</span>
});
<span class="hljs-keyword">throw</span> error;
}
}
Récupération d'erreur intelligente
En enrichissant les erreurs avec un contexte détaillé, vous pouvez prendre des décisions éclairées sur la manière de les corriger. Par exemple, vous pouvez réessayer une opération, nettoyer les données ou mettre le problème en file d'attente pour une vérification manuelle :
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handleEnrichedError</span>(<span class="hljs-params">error, originalOperation, originalData</span>) {
<span class="hljs-keyword">const</span> context = error.<span class="hljs-property">context</span> || {};
<span class="hljs-keyword">const</span> workflowMetadata = error.<span class="hljs-property">workflowMetadata</span> || {};
<span class="hljs-comment">// Retry for network issues during API calls</span>
<span class="hljs-keyword">if</span> (context.<span class="hljs-property">operation</span> === <span class="hljs-string">'external_api_call'</span> && error.<span class="hljs-property">originalError</span>?.<span class="hljs-property">code</span> === <span class="hljs-string">'ECONNRESET'</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Network error detected in <span class="hljs-subst">${context.step}</span>, retrying...`</span>);
<span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, <span class="hljs-number">2000</span>));
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">originalOperation</span>(originalData);
}
<span class="hljs-comment">// Attempt cleanup for validation errors</span>
<span class="hljs-keyword">if</span> (context.<span class="hljs-property">operation</span> === <span class="hljs-string">'data_validation'</span> && workflowMetadata.<span class="hljs-property">retryable</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Data validation failed, attempting cleanup...'</span>);
<span class="hljs-keyword">const</span> cleanedData = <span class="hljs-keyword">await</span> <span class="hljs-title function_">cleanupData</span>(originalData);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">originalOperation</span>(cleanedData);
}
<span class="hljs-comment">// Queue long-running workflows for manual review</span>
<span class="hljs-keyword">if</span> (workflowMetadata.<span class="hljs-property">totalExecutionTime</span> > <span class="hljs-number">30000</span>) { <span class="hljs-comment">// 30 seconds</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Long-running workflow failed, queuing for manual review...'</span>);
<span class="hljs-keyword">await</span> <span class="hljs-title function_">queueForReview</span>({
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">workflowMetadata</span>: workflowMetadata
});
}
<span class="hljs-keyword">throw</span> error;
}
sbb-itb-23997f1
4. Stratégies automatisées de nouvelle tentative et de retour en arrière
Les mécanismes de relance automatisés avec stratégies de backoff jouent un rôle essentiel dans le maintien de la résilience des workflows. Ces méthodes gèrent automatiquement les problèmes transitoires tels que les interruptions de réseau, les limites de débit ou les contraintes temporaires de ressources. Elles contribuent également à prévenir la surcharge du système en augmentant progressivement les délais entre les relances, ce qui laisse aux systèmes le temps de se stabiliser.
Retard exponentiel Il s'agit d'une approche courante qui augmente le délai après chaque nouvelle tentative. Cette méthode garantit que les systèmes ne sont pas surchargés pendant la récupération.
<span class="hljs-keyword">class</span> <span class="hljs-title class_">RetryManager</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">options = {}</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">maxRetries</span> = options.<span class="hljs-property">maxRetries</span> || <span class="hljs-number">3</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">baseDelay</span> = options.<span class="hljs-property">baseDelay</span> || <span class="hljs-number">1000</span>; <span class="hljs-comment">// 1 second</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">maxDelay</span> = options.<span class="hljs-property">maxDelay</span> || <span class="hljs-number">30000</span>; <span class="hljs-comment">// 30 seconds</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">backoffMultiplier</span> = options.<span class="hljs-property">backoffMultiplier</span> || <span class="hljs-number">2</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">jitterRange</span> = options.<span class="hljs-property">jitterRange</span> || <span class="hljs-number">0.1</span>; <span class="hljs-comment">// 10% jitter</span>
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">executeWithRetry</span>(<span class="hljs-params">operation, context = {}</span>) {
<span class="hljs-keyword">let</span> lastError;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> attempt = <span class="hljs-number">0</span>; attempt <= <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxRetries</span>; attempt++) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>();
<span class="hljs-comment">// Log successful retries</span>
<span class="hljs-keyword">if</span> (attempt > <span class="hljs-number">0</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Operation succeeded on attempt <span class="hljs-subst">${attempt + <span class="hljs-number">1</span>}</span>`</span>, {
<span class="hljs-attr">context</span>: context,
<span class="hljs-attr">totalAttempts</span>: attempt + <span class="hljs-number">1</span>,
<span class="hljs-attr">recoveryTime</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>() - context.<span class="hljs-property">startTime</span>
});
}
<span class="hljs-keyword">return</span> result;
} <span class="hljs-keyword">catch</span> (error) {
lastError = error;
<span class="hljs-comment">// Stop retrying if the error is non-retryable or the max attempts are reached</span>
<span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">isRetryableError</span>(error) || attempt === <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxRetries</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">createFinalError</span>(error, attempt + <span class="hljs-number">1</span>, context);
}
<span class="hljs-keyword">const</span> delay = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">calculateDelay</span>(attempt);
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">`Attempt <span class="hljs-subst">${attempt + <span class="hljs-number">1</span>}</span> failed, retrying in <span class="hljs-subst">${delay}</span>ms`</span>, {
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
<span class="hljs-attr">context</span>: context,
<span class="hljs-attr">nextDelay</span>: delay
});
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">sleep</span>(delay);
}
}
}
<span class="hljs-title function_">calculateDelay</span>(<span class="hljs-params">attempt</span>) {
<span class="hljs-comment">// Exponential backoff with added jitter</span>
<span class="hljs-keyword">const</span> exponentialDelay = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">min</span>(
<span class="hljs-variable language_">this</span>.<span class="hljs-property">baseDelay</span> * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">backoffMultiplier</span>, attempt),
<span class="hljs-variable language_">this</span>.<span class="hljs-property">maxDelay</span>
);
<span class="hljs-keyword">const</span> jitter = exponentialDelay * <span class="hljs-variable language_">this</span>.<span class="hljs-property">jitterRange</span> * (<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">2</span> - <span class="hljs-number">1</span>);
<span class="hljs-keyword">return</span> <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">round</span>(exponentialDelay + jitter);
}
<span class="hljs-title function_">isRetryableError</span>(<span class="hljs-params">error</span>) {
<span class="hljs-comment">// Handle transient network errors</span>
<span class="hljs-keyword">if</span> (error.<span class="hljs-property">code</span> === <span class="hljs-string">'ECONNRESET'</span> || error.<span class="hljs-property">code</span> === <span class="hljs-string">'ETIMEDOUT'</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
<span class="hljs-comment">// Retry on specific HTTP status codes</span>
<span class="hljs-keyword">if</span> (error.<span class="hljs-property">response</span>?.<span class="hljs-property">status</span>) {
<span class="hljs-keyword">const</span> status = error.<span class="hljs-property">response</span>.<span class="hljs-property">status</span>;
<span class="hljs-keyword">return</span> [<span class="hljs-number">429</span>, <span class="hljs-number">502</span>, <span class="hljs-number">503</span>, <span class="hljs-number">504</span>].<span class="hljs-title function_">includes</span>(status);
}
<span class="hljs-comment">// Check for database connection issues</span>
<span class="hljs-keyword">if</span> (error.<span class="hljs-property">message</span>?.<span class="hljs-title function_">includes</span>(<span class="hljs-string">'connection'</span>) || error.<span class="hljs-property">message</span>?.<span class="hljs-title function_">includes</span>(<span class="hljs-string">'timeout'</span>)) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
<span class="hljs-title function_">createFinalError</span>(<span class="hljs-params">originalError, totalAttempts, context</span>) {
<span class="hljs-keyword">const</span> finalError = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Operation failed after <span class="hljs-subst">${totalAttempts}</span> attempts: <span class="hljs-subst">${originalError.message}</span>`</span>);
finalError.<span class="hljs-property">originalError</span> = originalError;
finalError.<span class="hljs-property">retryContext</span> = {
<span class="hljs-attr">totalAttempts</span>: totalAttempts,
<span class="hljs-attr">finalAttemptTime</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(),
<span class="hljs-attr">context</span>: context,
<span class="hljs-attr">wasRetryable</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">isRetryableError</span>(originalError)
};
<span class="hljs-keyword">return</span> finalError;
}
<span class="hljs-title function_">sleep</span>(<span class="hljs-params">ms</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =></span> <span class="hljs-built_in">setTimeout</span>(resolve, ms));
}
}
Ce système de nouvelle tentative s'intègre parfaitement dans des cadres de récupération d'erreurs plus larges, garantissant stabilité et efficacité.
Implémentation du modèle de disjoncteur
Pour compléter les mécanismes de nouvelle tentative, le modèle de disjoncteur agit comme une protection contre les pannes répétées. En interrompant temporairement les opérations lorsque les taux d'erreur dépassent les seuils acceptables, il prévient les pannes en cascade et offre aux systèmes en difficulté une chance de se rétablir.
Voici un exemple de la manière dont cela peut être mis en œuvre :
<span class="hljs-keyword">class</span> <span class="hljs-title class_">CircuitBreaker</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">options = {}</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">failureThreshold</span> = options.<span class="hljs-property">failureThreshold</span> || <span class="hljs-number">5</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">recoveryTimeout</span> = options.<span class="hljs-property">recoveryTimeout</span> || <span class="hljs-number">60000</span>; <span class="hljs-comment">// 1 minute</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">monitoringWindow</span> = options.<span class="hljs-property">monitoringWindow</span> || <span class="hljs-number">120000</span>; <span class="hljs-comment">// 2 minutes</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> = <span class="hljs-string">'CLOSED'</span>; <span class="hljs-comment">// Possible states: CLOSED, OPEN, HALF_OPEN</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span> = <span class="hljs-number">0</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">lastFailureTime</span> = <span class="hljs-literal">null</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">successCount</span> = <span class="hljs-number">0</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">requestHistory</span> = [];
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">execute</span>(<span class="hljs-params">operation, context = {}</span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> === <span class="hljs-string">'OPEN'</span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>() - <span class="hljs-variable language_">this</span>.<span class="hljs-property">lastFailureTime</span> >= <span class="hljs-variable language_">this</span>.<span class="hljs-property">recoveryTimeout</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> = <span class="hljs-string">'HALF_OPEN'</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">successCount</span> = <span class="hljs-number">0</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Circuit breaker transitioning to HALF_OPEN state'</span>, { context });
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Circuit breaker is OPEN. Service unavailable. Retry after <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-variable language_">this</span>.lastFailureTime + <span class="hljs-variable language_">this</span>.recoveryTimeout).toLocaleString()}</span>`</span>);
}
}
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>();
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">onSuccess</span>(context);
<span class="hljs-keyword">return</span> result;
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">onFailure</span>(error, context);
<span class="hljs-keyword">throw</span> error;
}
}
<span class="hljs-title function_">onSuccess</span>(<span class="hljs-params">context</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">recordRequest</span>(<span class="hljs-literal">true</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> === <span class="hljs-string">'HALF_OPEN'</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">successCount</span>++;
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">successCount</span> >= <span class="hljs-number">3</span>) { <span class="hljs-comment">// Require three consecutive successes to close the breaker</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> = <span class="hljs-string">'CLOSED'</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span> = <span class="hljs-number">0</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Circuit breaker CLOSED after successful recovery'</span>, { context });
}
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> === <span class="hljs-string">'CLOSED'</span>) {
<span class="hljs-comment">// Gradually reduce failure count during normal operation</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">max</span>(<span class="hljs-number">0</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span> - <span class="hljs-number">1</span>);
}
}
<span class="hljs-title function_">onFailure</span>(<span class="hljs-params">error, context</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">recordRequest</span>(<span class="hljs-literal">false</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span>++;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">lastFailureTime</span> = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> === <span class="hljs-string">'HALF_OPEN'</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> = <span class="hljs-string">'OPEN'</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Circuit breaker OPEN after failure in HALF_OPEN state'</span>, {
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
context
});
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> === <span class="hljs-string">'CLOSED'</span> && <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span> >= <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureThreshold</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span> = <span class="hljs-string">'OPEN'</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Circuit breaker OPEN due to failure threshold exceeded'</span>, {
<span class="hljs-attr">failureCount</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span>,
<span class="hljs-attr">threshold</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureThreshold</span>,
context
});
}
}
<span class="hljs-title function_">recordRequest</span>(<span class="hljs-params">success</span>) {
<span class="hljs-keyword">const</span> now = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();
<span class="hljs-variable language_">this</span>.<span class="hljs-property">requestHistory</span>.<span class="hljs-title function_">push</span>({ <span class="hljs-attr">timestamp</span>: now, success });
<span class="hljs-comment">// Discard records outside the monitoring window</span>
<span class="hljs-variable language_">this</span>.<span class="hljs-property">requestHistory</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">requestHistory</span>.<span class="hljs-title function_">filter</span>(
<span class="hljs-function"><span class="hljs-params">record</span> =></span> now - record.<span class="hljs-property">timestamp</span> <= <span class="hljs-variable language_">this</span>.<span class="hljs-property">monitoringWindow</span>
);
}
<span class="hljs-title function_">getHealthMetrics</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">const</span> now = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();
<span class="hljs-keyword">const</span> recentRequests = <span class="hljs-variable language_">this</span>.<span class="hljs-property">requestHistory</span>.<span class="hljs-title function_">filter</span>(
<span class="hljs-function"><span class="hljs-params">record</span> =></span> now - record.<span class="hljs-property">timestamp</span> <= <span class="hljs-variable language_">this</span>.<span class="hljs-property">monitoringWindow</span>
);
<span class="hljs-keyword">const</span> totalRequests = recentRequests.<span class="hljs-property">length</span>;
<span class="hljs-keyword">const</span> successfulRequests = recentRequests.<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.<span class="hljs-property">success</span>).<span class="hljs-property">length</span>;
<span class="hljs-keyword">const</span> failureRate = totalRequests > <span class="hljs-number">0</span> ? (totalRequests - successfulRequests) / totalRequests : <span class="hljs-number">0</span>;
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">state</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>,
<span class="hljs-attr">failureCount</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">failureCount</span>,
totalRequests,
successfulRequests,
<span class="hljs-attr">failureRate</span>: <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">round</span>(failureRate * <span class="hljs-number">100</span>) / <span class="hljs-number">100</span>,
<span class="hljs-attr">lastFailureTime</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">lastFailureTime</span> ? <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">lastFailureTime</span>).<span class="hljs-title function_">toLocaleString</span>() : <span class="hljs-string">'N/A'</span>
Cette approche garantit que les systèmes défaillants ne sont pas submergés, tout en offrant une voie de récupération claire. Ensemble, les mécanismes de nouvelle tentative et les disjoncteurs créent une base solide pour la gestion des erreurs dans les systèmes distribués.
5. Préservation et restauration de l'état du flux de travail
Préserver l'état d'un workflow à des moments cruciaux permet une récupération efficace des erreurs sans avoir à redémarrer l'ensemble du processus. Cette approche est particulièrement utile pour les workflows impliquant de multiples interactions système, des transformations de données complexes ou des tâches longues dont l'exécution incomplète peut entraîner des incohérences.
En sauvegardant des instantanés des données de workflow, des états système et des contextes d'exécution à des moments précis, les mécanismes de restauration permettent de restaurer ces états sauvegardés. Cela garantit la reprise des processus à partir d'un point stable et fiable. Voici un exemple de mise en œuvre de la préservation d'état et de la restauration en JavaScript :
<span class="hljs-keyword">class</span> <span class="hljs-title class_">WorkflowStateManager</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">options = {}</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Map</span>();
<span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span> = [];
<span class="hljs-variable language_">this</span>.<span class="hljs-property">maxStateHistory</span> = options.<span class="hljs-property">maxStateHistory</span> || <span class="hljs-number">10</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">compressionEnabled</span> = options.<span class="hljs-property">compressionEnabled</span> || <span class="hljs-literal">false</span>;
<span class="hljs-variable language_">this</span>.<span class="hljs-property">persistentStorage</span> = options.<span class="hljs-property">persistentStorage</span> || <span class="hljs-literal">null</span>;
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">saveCheckpoint</span>(<span class="hljs-params">checkpointId, workflowData, metadata = {}</span>) {
<span class="hljs-keyword">const</span> checkpoint = {
<span class="hljs-attr">id</span>: checkpointId,
<span class="hljs-attr">timestamp</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>(),
<span class="hljs-attr">data</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">deepClone</span>(workflowData),
<span class="hljs-attr">metadata</span>: {
...metadata,
<span class="hljs-attr">version</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">generateVersion</span>(),
<span class="hljs-attr">size</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(workflowData).<span class="hljs-property">length</span>
}
};
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">compressionEnabled</span> && checkpoint.<span class="hljs-property">metadata</span>.<span class="hljs-property">size</span> > <span class="hljs-number">10000</span>) {
checkpoint.<span class="hljs-property">data</span> = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">compressState</span>(checkpoint.<span class="hljs-property">data</span>);
checkpoint.<span class="hljs-property">compressed</span> = <span class="hljs-literal">true</span>;
}
<span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">set</span>(checkpointId, checkpoint);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-title function_">push</span>(checkpointId);
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-property">length</span> > <span class="hljs-variable language_">this</span>.<span class="hljs-property">maxStateHistory</span>) {
<span class="hljs-keyword">const</span> oldestId = <span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-title function_">shift</span>();
<span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">delete</span>(oldestId);
}
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">persistentStorage</span>) {
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">persistentStorage</span>.<span class="hljs-title function_">save</span>(checkpointId, checkpoint);
}
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Checkpoint <span class="hljs-subst">${checkpointId}</span> saved`</span>, {
<span class="hljs-attr">size</span>: checkpoint.<span class="hljs-property">metadata</span>.<span class="hljs-property">size</span>,
<span class="hljs-attr">compressed</span>: checkpoint.<span class="hljs-property">compressed</span> || <span class="hljs-literal">false</span>
});
<span class="hljs-keyword">return</span> checkpoint.<span class="hljs-property">metadata</span>.<span class="hljs-property">version</span>;
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">rollbackToCheckpoint</span>(<span class="hljs-params">checkpointId, options = {}</span>) {
<span class="hljs-keyword">const</span> checkpoint = <span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">get</span>(checkpointId);
<span class="hljs-keyword">if</span> (!checkpoint) {
<span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">persistentStorage</span>) {
<span class="hljs-keyword">const</span> persistedCheckpoint = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">persistentStorage</span>.<span class="hljs-title function_">load</span>(checkpointId);
<span class="hljs-keyword">if</span> (persistedCheckpoint) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">set</span>(checkpointId, persistedCheckpoint);
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">executeRollback</span>(persistedCheckpoint, options);
}
}
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Checkpoint <span class="hljs-subst">${checkpointId}</span> not found`</span>);
}
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">executeRollback</span>(checkpoint, options);
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">executeRollback</span>(<span class="hljs-params">checkpoint, options</span>) {
<span class="hljs-keyword">const</span> rollbackStart = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>();
<span class="hljs-keyword">let</span> removedCount = <span class="hljs-number">0</span>;
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">let</span> restoredData = checkpoint.<span class="hljs-property">data</span>;
<span class="hljs-keyword">if</span> (checkpoint.<span class="hljs-property">compressed</span>) {
restoredData = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">decompressState</span>(checkpoint.<span class="hljs-property">data</span>);
}
<span class="hljs-keyword">if</span> (options.<span class="hljs-property">cleanupOperations</span>) {
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">executeCleanupOperations</span>(options.<span class="hljs-property">cleanupOperations</span>);
}
<span class="hljs-keyword">const</span> targetIndex = <span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-title function_">indexOf</span>(checkpoint.<span class="hljs-property">id</span>);
<span class="hljs-keyword">if</span> (targetIndex !== -<span class="hljs-number">1</span>) {
<span class="hljs-keyword">const</span> checkpointsToRemove = <span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-title function_">splice</span>(targetIndex + <span class="hljs-number">1</span>);
checkpointsToRemove.<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">id</span> =></span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">delete</span>(id));
removedCount = checkpointsToRemove.<span class="hljs-property">length</span>;
}
<span class="hljs-keyword">const</span> rollbackDuration = <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>() - rollbackStart;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Rollback completed: <span class="hljs-subst">${checkpoint.id}</span>`</span>, {
<span class="hljs-attr">rollbackTime</span>: rollbackDuration,
<span class="hljs-attr">restoredDataSize</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(restoredData).<span class="hljs-property">length</span>,
<span class="hljs-attr">checkpointsRemoved</span>: removedCount,
<span class="hljs-attr">originalTimestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(checkpoint.<span class="hljs-property">timestamp</span>).<span class="hljs-title function_">toLocaleString</span>()
});
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">data</span>: restoredData,
<span class="hljs-attr">metadata</span>: checkpoint.<span class="hljs-property">metadata</span>,
rollbackDuration
};
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">`Rollback failed for checkpoint <span class="hljs-subst">${checkpoint.id}</span>:`</span>, error);
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Rollback operation failed: <span class="hljs-subst">${error.message}</span>`</span>);
}
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">createTransactionalScope</span>(<span class="hljs-params">scopeName, operation</span>) {
<span class="hljs-keyword">const</span> transactionId = <span class="hljs-string">`<span class="hljs-subst">${scopeName}</span>_<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>`</span>;
<span class="hljs-keyword">const</span> initialState = <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">captureCurrentState</span>();
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">saveCheckpoint</span>(<span class="hljs-string">`pre_<span class="hljs-subst">${transactionId}</span>`</span>, initialState, {
<span class="hljs-attr">transactionScope</span>: scopeName,
<span class="hljs-attr">type</span>: <span class="hljs-string">'transaction_start'</span>
});
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>();
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">saveCheckpoint</span>(<span class="hljs-string">`post_<span class="hljs-subst">${transactionId}</span>`</span>, result, {
<span class="hljs-attr">transactionScope</span>: scopeName,
<span class="hljs-attr">type</span>: <span class="hljs-string">'transaction_complete'</span>
});
<span class="hljs-keyword">return</span> result;
} <span class="hljs-keyword">catch</span> (error) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">`Transaction <span class="hljs-subst">${scopeName}</span> failed, initiating rollback`</span>, {
<span class="hljs-attr">error</span>: error.<span class="hljs-property">message</span>,
transactionId
});
<span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">rollbackToCheckpoint</span>(<span class="hljs-string">`pre_<span class="hljs-subst">${transactionId}</span>`</span>, {
<span class="hljs-attr">cleanupOperations</span>: <span class="hljs-keyword">await</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getTransactionCleanup</span>(scopeName)
});
<span class="hljs-keyword">throw</span> error;
}
}
<span class="hljs-title function_">deepClone</span>(<span class="hljs-params">obj</span>) {
<span class="hljs-keyword">if</span> (obj === <span class="hljs-literal">null</span> || <span class="hljs-keyword">typeof</span> obj !== <span class="hljs-string">'object'</span>) <span class="hljs-keyword">return</span> obj;
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Date</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(obj.<span class="hljs-title function_">getTime</span>());
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Array</span>) <span class="hljs-keyword">return</span> obj.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">item</span> =></span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">deepClone</span>(item));
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> obj === <span class="hljs-string">'object'</span>) {
<span class="hljs-keyword">const</span> cloned = {};
<span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(obj).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">key</span> =></span> {
cloned[key] = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">deepClone</span>(obj[key]);
});
<span class="hljs-keyword">return</span> cloned;
}
}
<span class="hljs-title function_">generateVersion</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">return</span> <span class="hljs-string">`v<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>_<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">36</span>).substr(<span class="hljs-number">2</span>, <span class="hljs-number">9</span>)}</span>`</span>;
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">captureCurrentState</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">timestamp</span>: <span class="hljs-title class_">Date</span>.<span class="hljs-title function_">now</span>()
};
}
<span class="hljs-keyword">async</span> <span class="hljs-title function_">executeCleanupOperations</span>(<span class="hljs-params">operations</span>) {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> operation <span class="hljs-keyword">of</span> operations) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">await</span> <span class="hljs-title function_">operation</span>();
} <span class="hljs-keyword">catch</span> (cleanupError) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">warn</span>(<span class="hljs-string">'Cleanup operation failed:'</span>, cleanupError.<span class="hljs-property">message</span>);
}
}
}
<span class="hljs-title function_">getStateHistory</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">rollbackStack</span>.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">id</span> =></span> {
<span class="hljs-keyword">const</span> checkpoint = <span class="hljs-variable language_">this</span>.<span class="hljs-property">stateStorage</span>.<span class="hljs-title function_">get</span>(id);
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">id</span>: checkpoint.<span class="hljs-property">id</span>,
<span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(checkpoint.<span class="hljs-property">timestamp</span>).<span class="hljs-title function_">toLocaleString</span>(),
<span class="hljs-attr">size</span>: checkpoint.<span class="hljs-property">metadata</span>.<span class="hljs-property">size</span>,
<span class="hljs-attr">compressed</span>: checkpoint.<span class="hljs-property">compressed</span> || <span class="hljs-literal">false</span>,
<span class="hljs-attr">metadata</span>: checkpoint.<span class="hljs-property">metadata</span>
};
});
}
}
Des plateformes comme Latenode facilitent l'intégration de mécanismes robustes de récupération d'erreurs dans les workflows d'automatisation. En appliquant des techniques de préservation d'état et de restauration, comme illustré ci-dessus, vous pouvez créer des processus JavaScript résilients qui préservent l'intégrité des données, même en cas de problèmes inattendus. Cette approche complète les stratégies de gestion des erreurs en garantissant la continuité des workflows.
Tableau de comparaison
JavaScript propose plusieurs techniques de récupération d'erreur, chacune adaptée à des besoins et des scénarios spécifiques. Le choix de la méthode la plus adaptée dépend de la compréhension de leurs points forts, de leurs limites et de leur adéquation avec les exigences de votre workflow.
| Technique | Efficacité de la récupération des erreurs | Complexité de la mise en œuvre | Idéal pour | Impact sur les performances | Courbe de progression |
|---|---|---|---|---|---|
| Try-Catch et gestion des erreurs asynchrones | Élevé pour les erreurs prévisibles | Faible | Appels d'API, opérations de base de données, E/S de fichiers | Frais généraux minimaux | Convient aux débutants |
| Classes d'erreur personnalisées | Très élevé pour les erreurs catégorisées | Moyenne | Flux de travail en plusieurs étapes, applications orientées utilisateur | Frais généraux bas | Intermédiaire |
| Relancement des erreurs et enrichissement du contexte | Idéal pour le débogage de flux complexes | Moyen-élevé | Appels de fonctions imbriquées, microservices | Frais généraux modérés | Intermédiaire avancé |
| Stratégies automatisées de nouvelle tentative et de retour en arrière | Excellent pour les pannes transitoires | Élevée | Demandes réseau, appels de services externes | Frais généraux modérés | Avancé |
| Préservation et restauration de l'état du flux de travail | Excellent pour l'intégrité des données | Très élevé | Processus de longue durée, transactions financières | Frais généraux élevés | Avancé |
Chacune de ces techniques joue un rôle spécifique dans la création de flux de travail résilients et fiables. Vous trouverez ci-dessous un aperçu détaillé de leur fonctionnement et de leur utilisation.
Try-Catch et gestion des erreurs asynchrones est l'option idéale pour une gestion rapide et simple des erreurs. Particulièrement utile pour gérer les erreurs prévisibles dans des tâches telles que les appels d'API ou les opérations sur les fichiers, elle nécessite une configuration minimale et offre une grande simplicité d'utilisation.
Classes d'erreur personnalisées Ils sont particulièrement efficaces lorsque les workflows doivent différencier plusieurs types d'erreurs. En catégorisant les erreurs, ils permettent des stratégies de récupération ciblées, idéales pour les applications complexes ou les systèmes orientés utilisateur.
Relancement des erreurs et enrichissement du contexte est indispensable pour déboguer des workflows complexes. En contextualisant les erreurs au fur et à mesure de leur propagation, cette technique permet de remonter à leur origine, ce qui est particulièrement utile pour les appels de fonctions imbriqués ou les microservices.
Stratégies automatisées de nouvelle tentative et de retour en arrière Gérez efficacement les problèmes temporaires, tels que les dépassements de délai réseau ou les pannes de services externes. La configuration des nouvelles tentatives avec des intervalles de temporisation garantit la stabilité, mais une configuration minutieuse est essentielle pour éviter les retards inutiles.
Préservation et restauration de l'état du flux de travail Assure l'intégrité des données dans les opérations à enjeux élevés. La gestion des points de contrôle et la restauration des états antérieurs en cas d'erreur sont particulièrement utiles pour les processus de longue durée ou les transactions financières exigeant de la précision.
Lors de la conception de workflows d'automatisation dans Latenode, ces techniques peuvent être combinées pour une efficacité maximale. Par exemple, vous pouvez utiliser try-catch pour la gestion des erreurs de base, intégrer des classes d'erreur personnalisées pour les échecs spécifiques au workflow et appliquer la préservation de l'état pour les opérations critiques. Cette approche en couches garantit une récupération d'erreur robuste sans complexifier les tâches plus simples.
En fin de compte, la clé d'une gestion efficace des erreurs réside dans l'adéquation de la complexité de votre stratégie de récupération aux besoins de votre workflow. Par exemple, une simple transformation de données peut se limiter à une gestion try-catch, tandis qu'une intégration en plusieurs étapes impliquant des données sensibles exige une approche plus globale. En adaptant ces techniques à votre situation spécifique, vous pouvez allier fiabilité et efficacité.
Conclusion
Une stratégie de défense multicouche est essentielle pour garantir la fiabilité des workflows d'automatisation, notamment pour la récupération d'erreurs dans l'automatisation basée sur JavaScript. La combinaison de plusieurs techniques crée un cadre robuste pour gérer efficacement les erreurs. blocs try-catch pour une limitation immédiate des erreurs classes d'erreur personnalisées qui ajoutent un contexte de débogage précieux, chaque méthode joue un rôle essentiel. Erreur lors de la relance préserve les traces de pile pour une meilleure analyse, stratégies de nouvelle tentative automatisées traiter les défaillances transitoires et préservation de l'État préserve l'intégrité des données lors d'opérations complexes. Une enquête menée en 2024 auprès d'ingénieurs en automatisation a révélé que 68 % considèrent la gestion robuste des erreurs comme le facteur le plus critique pour la fiabilité du flux de travail [1].
Dans les applications pratiques, ces méthodes fonctionnent parfaitement ensemble. Par exemple, les blocs try-catch peuvent gérer les échecs d'API en temps réel, tandis que les classes d'erreur personnalisées différencient les types d'erreurs. Les erreurs renvoyées, enrichies de contexte supplémentaire, améliorent la journalisation et le débogage. Les mécanismes de relance avec backoff exponentiel gèrent efficacement les problèmes temporaires, et la préservation de l'état garantit une récupération fluide des flux de travail sans perte de données. Les données du secteur suggèrent qu'une gestion structurée des erreurs peut réduire les temps d'arrêt imprévus dans les flux de travail jusqu'à 40 % [1][2].
Les plateformes qui prennent en charge les flux de travail visuels et basés sur le code sont essentielles pour mettre en œuvre ces stratégies de manière efficace. Laténode Latenode se distingue comme un outil puissant pour l'intégration de modèles de récupération d'erreurs. Son workflow visuel, associé à la prise en charge native de JavaScript, facilite l'intégration de la logique de gestion des erreurs par les développeurs. La base de données intégrée à la plateforme facilite la préservation de l'état, tandis que ses outils d'orchestration et de journalisation simplifient la surveillance et la récupération. Grâce aux intégrations complètes de Latenode, vous pouvez implémenter des mécanismes de nouvelle tentative sur divers services externes tout en conservant une gestion centralisée des erreurs.
Le succès des stratégies de récupération d'erreurs dépend de l'adaptation de leur complexité aux besoins spécifiques de votre workflow. Pour des tâches plus simples comme les transformations de données, des blocs try-catch peuvent suffire. Cependant, des processus plus complexes, comme les intégrations en plusieurs étapes impliquant des données sensibles, nécessitent une approche globale intégrant les cinq techniques. En exploitant des plateformes intégrant des workflows visuels et basés sur du code, vous pouvez créer des systèmes d'automatisation résilients qui non seulement gèrent les erreurs avec élégance, mais s'adaptent et évoluent avec l'évolution de vos besoins.
Questions Fréquentes Posées
Quels sont les avantages de l’utilisation de classes d’erreur personnalisées dans les workflows JavaScript ?
Les classes d'erreur personnalisées en JavaScript permettent de gérer plus efficacement les erreurs en définissant des types d'erreur spécifiques adaptés à différents scénarios. Cette méthode améliore la clarté et la structure de la gestion des erreurs, facilitant ainsi l'identification de l'origine d'un problème. L'utilisation d'outils tels que instanceof, vous pouvez déterminer précisément le type d'erreur rencontrée.
L'intégration de classes d'erreurs personnalisées simplifie le débogage, améliore la lisibilité du code et simplifie la gestion des flux de travail. Cette approche garantit une gestion cohérente des erreurs, particulièrement utile pour la maintenance de systèmes complexes ou de processus d'automatisation.
Quels sont les avantages de l’utilisation de stratégies de nouvelle tentative et de backoff pour la récupération d’erreurs dans les workflows JavaScript ?
Les stratégies de relance et de backoff jouent un rôle essentiel pour garantir la fiabilité et la robustesse des workflows JavaScript. Ces méthodes permettent aux systèmes de récupérer automatiquement après des erreurs temporaires, réduisant ainsi les temps d'arrêt et limitant le recours au dépannage manuel.
Une technique largement utilisée, ralentissement exponentiel, espace les tentatives de relance en augmentant progressivement le délai entre chacune. Cette approche permet d'éviter la surcharge du système, de réduire la congestion du réseau et d'optimiser l'utilisation des ressources. Grâce à ces stratégies, les systèmes peuvent gérer plus efficacement les pannes temporaires, améliorant ainsi les performances et l'expérience utilisateur globale.
Comment la préservation des états du flux de travail et l’utilisation des restaurations améliorent-elles la fiabilité de l’automatisation ?
La préservation des états des workflows et la mise en œuvre de restaurations jouent un rôle crucial pour garantir la fiabilité des processus d'automatisation. Ces stratégies permettent aux systèmes de revenir à un état stable en cas d'erreur, minimisant ainsi les perturbations et empêchant les mises à jour erronées d'affecter les opérations en direct. Cette approche garantit une reprise plus fluide et un fonctionnement efficace des processus.
Les mécanismes de restauration automatisés sont particulièrement utiles car ils réduisent le recours aux interventions manuelles, assurent la continuité opérationnelle et renforcent la résilience des systèmes. En préservant les flux de travail essentiels, ces pratiques contribuent à créer des solutions d'automatisation à la fois robustes et fiables.
À lire également
- Débogage des scripts Puppeteer : du mode ralenti aux techniques avancées
- Conception de workflows JavaScript résistants aux erreurs
- JavaScript personnalisé dans les outils de workflow visuel
- Gestion des erreurs dans les workflows low-code : bonnes pratiques



