diff --git a/microsoft/knowledge/performance/job-queue-category-code-serializes-conflicting-jobs.md b/microsoft/knowledge/performance/job-queue-category-code-serializes-conflicting-jobs.md index 7b1b1c0..d92647e 100644 --- a/microsoft/knowledge/performance/job-queue-category-code-serializes-conflicting-jobs.md +++ b/microsoft/knowledge/performance/job-queue-category-code-serializes-conflicting-jobs.md @@ -13,11 +13,11 @@ application-area: [all] ## Description -Different job queue entries can run at the same time. When two jobs update the same exclusive resource, concurrent execution can cause lock contention, deadlocks, or conflicting results. Entries with the same Job Queue Category Code are serialized: while one runs, another entry in that category waits. +Different job queue entries can run at the same time. When two jobs update the same exclusive resource, concurrent execution can cause lock contention, deadlocks, or conflicting results. Within one company, entries with the same Job Queue Category Code are serialized: while one runs, another entry in that category waits. ## Best Practice -Assign the same non-empty Job Queue Category Code to jobs that must not overlap, regardless of which codeunit they run. Define categories around the shared resource or exclusivity requirement, not merely around object names. Leave independent jobs in different categories so they can still run concurrently. +Assign the same non-empty Job Queue Category Code to job queue entries in the same company that must not overlap, regardless of which codeunit they run. Define categories around the shared resource or exclusivity requirement, not merely around object names. Leave independent jobs in different categories so they can still run concurrently. A category does not serialize work across companies or environments, or coordinate workers outside the job queue dispatcher. Protect shared external or cross-company resources with a separate application-level locking mechanism. See sample: `job-queue-category-code-serializes-conflicting-jobs.good.al`. diff --git a/microsoft/knowledge/performance/job-queue-external-effects-must-be-idempotent.md b/microsoft/knowledge/performance/job-queue-external-effects-must-be-idempotent.md index 771234a..a5932df 100644 --- a/microsoft/knowledge/performance/job-queue-external-effects-must-be-idempotent.md +++ b/microsoft/knowledge/performance/job-queue-external-effects-must-be-idempotent.md @@ -13,7 +13,7 @@ application-area: [all] ## Description -A job queue handler can successfully create something in an external system and then fail while updating Business Central. Business Central rolls back its database changes and retries the queued work, but it cannot roll back the external request. Without a way for the external system to recognize the repeated request, the retry can create a duplicate shipment, payment, notification, or other side effect. +A job queue handler can successfully create something in an external system and then fail while updating Business Central. Business Central rolls back its database changes, but it cannot roll back the external request. The same work can later run again through configured retries, recurrence, rescheduling, or manual restart. Without a way for the external system to recognize the repeated request, a later run can create a duplicate shipment, payment, notification, or other side effect. ## Best Practice @@ -25,6 +25,6 @@ See sample: `job-queue-external-effects-must-be-idempotent.good.al`. ## Anti Pattern -Sending a state-changing request from a job queue handler with no stable request ID understood by the external API. Specifically, look for this sequence: read an outbox row, call `HttpClient.Post` or another side-effecting API, update or delete local data, and propagate an error that can cause the same outbox row to be retried. The key may be part of the request body, URI, headers, or an existing business key; a naturally idempotent remote operation is already safe and should not be flagged. +Sending a state-changing request from a job queue handler with no stable request ID understood by the external API. Specifically, look for this sequence: read an outbox row, call `HttpClient.Post` or another side-effecting API, update or delete local data, and propagate an error after which the same outbox row can be processed again. The key may be part of the request body, URI, headers, or an existing business key; a naturally idempotent remote operation is already safe and should not be flagged. See sample: `job-queue-external-effects-must-be-idempotent.bad.al`. \ No newline at end of file diff --git a/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.good.al b/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.good.al index bdd6a70..b924297 100644 --- a/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.good.al +++ b/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.good.al @@ -27,10 +27,9 @@ codeunit 50114 "Job Queue On Hold Good" trigger OnRun() begin - repeat + while not IsStopRequested(Rec.ID) do if not ProcessNextBatch() then exit; - until IsStopRequested(Rec.ID); end; local procedure IsStopRequested(JobQueueEntryId: Guid): Boolean diff --git a/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.md b/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.md index ef8ac57..6eec05c 100644 --- a/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.md +++ b/microsoft/knowledge/performance/job-queue-on-hold-does-not-stop-running-work.md @@ -17,7 +17,7 @@ The On Hold status prevents a job queue entry from starting again, but it does n ## Best Practice -Use On Hold to pause future scheduling. When a long-running operation must support graceful cancellation, store a separate application-owned stop request and check it between bounded units of work. Exit only at a point where completed work and the checkpoint are consistent; use administrative session termination only when graceful cancellation is impossible. +Use On Hold to pause future scheduling. When a long-running operation must support graceful cancellation, store a separate application-owned stop request and check it before every bounded unit of work, including the first. Exit only at a point where completed work and the checkpoint are consistent. The code that resumes scheduling must clear the stop request before restarting the job. Use administrative session termination only when graceful cancellation is impossible. See sample: `job-queue-on-hold-does-not-stop-running-work.good.al`. diff --git a/microsoft/knowledge/performance/store-scheduled-task-id-to-avoid-duplicate-tasks.good.al b/microsoft/knowledge/performance/store-scheduled-task-id-to-avoid-duplicate-tasks.good.al index 650bbc5..df52c62 100644 --- a/microsoft/knowledge/performance/store-scheduled-task-id-to-avoid-duplicate-tasks.good.al +++ b/microsoft/knowledge/performance/store-scheduled-task-id-to-avoid-duplicate-tasks.good.al @@ -1,6 +1,6 @@ codeunit 50115 "Scheduled Task Duplicate Good" { - procedure EnsureCleanupTask() + internal procedure EnsureCleanupTask() var TaskId: Guid; StoredTaskId: Text;