Batch size in Opportunity Product sync
Batch size controls how many quote records the Salesforce Database.executeBatch framework passes to each execute() invocation in the Opportunity Product Sync (Opp Prod Sync) process. The batch size applies consistently to all entry points that trigger Opp Prod Sync, but its practical impact depends on how many quotes are processed in a single request.
Batch size usage
All Opp Prod Sync entry points converge on a single batch implementation that determines how many quotes are processed per execute() call.
The core batch invocation is:
Database.executeBatch(new ChargeSegmentOppProdSyncBatch(quoteIds), scope)
where scope = OppProdSyncConfigAccessor.getEffectiveBatchScope(). The scope parameter defines how many quote IDs from the iterable are passed into each batch execute() chunk.
The iterable is the list of quote IDs that require synchronization. Regardless of how these quotes are enqueued (manual, auto-sync, or global methods), they ultimately run through the same batch implementation
Entry points and execution flow
Every Opp Prod Sync entry point flows into the same batch class and therefore uses the same batch size value.
High-level flow:
One of several entry points initiates a sync request:
Manual button: OppProdSyncExecutionController
Auto-sync triggers: OppProdSyncAutoSyncService
Queueable worker: OppProdSyncRequestQueueable
Global method v1: OppProdSyncGlobalService
All entry points ultimately call
OppProdSyncExecutionService.requestSyncForQuotes.
OppProdSyncExecutionService calls
ChargeSegmentOppProdSyncBatch.enqueueQuotes, which invokes Database.executeBatch with the configured batch size.
For each execute() chunk (a subset of the quote IDs list),
ChargeSegmentOppProdSyncService.syncOpportunityProducts runs per quote in that chunk.
The following figure shows how the various entry points converge on the same batch implementation. All Opp Prod Sync entry points converge to a single batch that chunks quote IDs by batch scope.
Impact of batch size on auto-sync triggers
The batch size setting applies to all paths into the batch, not only auto-sync triggers. However, its impact depends on how many quotes each entry point passes into a single request.
Trigger-level relevance
The following table summarizes when batch size is effectively relevant:
| Trigger / Entry point | Typical number of quotes | Batch size relevance |
|---|---|---|
| Submit Exit (enqueueAfterSubmitExitFromStudio) | Always 1 quote | No – size has no impact for a single-element batch. |
| Submit to Zuora (requestSyncBeforeQuotesSentToZuora) | Typically 1 (SSQ), can be multiple on bulk send | Rarely – only relevant when bulk send passes multiple quotes in one request. |
| Approval (enqueueIfApprovalAutoSyncTriggered) | 1 or more; bulk approval can affect many at once | Yes – batch approval that moves N quotes to Approved uses size to chunk them. |
| Manual button (OppProdSyncExecutionController) | Always 1 quote | No – always a single-quote sync. |
| Global method v1 (OppProdSyncGlobalService.syncQuotes) | N quotes (caller-determined) | Yes – primary scenario where size controls chunk size across many quote IDs. |
Single Subscription Quoting (SSQ)
For SSQ, batch size only materially affects scenarios where multiple quote IDs are passed in a single call into the batch.
Batch size matters for SSQ in:
Global method calls
When external code calls OppProdSyncGlobalService.syncQuotes() with multiple quote IDs, size determines how many of those IDs run in each execute() chunk.
Batch approval
When an approval process simultaneously transitions multiple quotes to Approved, batch size controls how those quotes are chunked in the batch execution.
There is no other point in the SSQ flow where batch size limits how many quotes are processed. All SSQ paths converge to the same batch, and the size parameter is the only throttle on quotes per execute() call.
Multi Subscription Quoting
In MSQ for release 10.58, batch size does not limit the number of child quotes per parent. It controls only how many parent quotes are processed in each execute() chunk.
MSQ processing behavior
When MSQ quotes are synchronized:
Only primary parent quote IDs pass the initial gate (Primary__c == true). Child quotes are rejected with MSG_NOT_PRIMARY_QUOTE at this stage.
These primary parent quote IDs are passed into ChargeSegmentOppProdSyncBatch as the iterable.
Batch size determines how many parent quotes are included in each execute() chunk.
Inside ChargeSegmentOppProdSyncService.syncOpportunityProducts(parentQuoteId), the service calls resolveChargeSegmentQuoteScope(quote), which queries all child quotes where ParentQuote__c = :quote.Id and includes their charge segments in a single sync run.
All child quotes for a given parent are processed within that parent’s sync call. Batch size does not cap the number of children; it only caps the number of parents per chunk.
Example
If the global method synchronizes 10 parent MSQ quotes and size = 5:
Execute #1 processes parent quotes 1–5
Execute #2 processes parent quotes 6–10
Each parent quote's call to syncOpportunityProducts independently pulls all of its children, regardless of batch size.
Failure behavior
Per-quote error isolation
ChargeSegmentOppProdSyncBatch.execute() isolates failures on a per-quote basis by wrapping each quote’s sync call in a try-catch block:
for (Id quoteId : scope) {
try {
ChargeSegmentOppProdSyncService.syncOpportunityProducts(quoteId, pbeMapCache);
} catch (Exception e) {
// Log, add to errors list, collect failed quote for bulk update
failedQuotesToUpdate.add(new Quote__c(
Id = quoteId,
OppProdSyncStatus__c = 'Failed',
OppProdSyncError__c = e.getMessage().abbreviate(1000),
OppProdSyncLastDateTime__c = System.now()
));
}
}
Key behaviors:
Single quote failure does not fail the entire chunkIf one quote in a batch chunk fails, the remaining quotes in the same chunk continue to process. Each quote is independent within its own try-catch block.
Batch chunks are independent transactionsEach execute() invocation is its own transaction. If one chunk encountered an unhandled exception, subsequent chunks would still run. In practice, the per-quote try-catch logic prevents the execute() method itself from throwing and keeps the batch running.
MSQ child quotes are not batched directlyScenarios such as "10 child quotes, batch size 5, failure in 1 batch" do not apply to MSQ, because child quotes are not part of the batch iterable. The batch processes parent quotes; each parent then processes its children internally.
Status field updates on failure
When a quote fails during Opp Prod Sync, the batch sets the following fields on the quote record:
zqu__OppProdSyncStatus__c → 'Failed'
zqu__OppProdSyncError__c → Exception message, truncated to 1000 characters
zqu__OppProdSyncLastDateTime__c → System.now()
If the bulk DML operation that writes these failure updates fails, the exception is caught and logged to debug logs only. In this edge case, a quote can remain stuck in 'Sync Pending' even though a failure occurred.
Batch size defines how many quote IDs are processed per batch execute() call; it does not cap MSQ child quotes per parent.
All entry points (manual, auto-sync, global) share the same batch implementation and size setting.
Batch size is particularly relevant for global method calls and bulk approval scenarios that pass many quotes at once.
Per-quote try-catch handling isolates failures and surfaces them via status fields without stopping other quotes or subsequent batch chunks.