fix: improve Subscription Billing performance for large datasets#7776
Open
jeffreybulanadi wants to merge 1 commit intomicrosoft:mainfrom
Open
fix: improve Subscription Billing performance for large datasets#7776jeffreybulanadi wants to merge 1 commit intomicrosoft:mainfrom
jeffreybulanadi wants to merge 1 commit intomicrosoft:mainfrom
Conversation
…rosoft#7690) Root causes addressed: 1. Missing database indexes causing full table scans on 4M+ billing lines 2. No progress dialog in billing proposal creation (unusable at scale) 3. Window.Open/Close/Update calls not guarded with GuiAllowed (Job Queue failures) 4. GetBillingLineNo() called inside UsageDataBilling inner loop (N^2 queries) 5. CustomerContract/VendorContract.Get() called for every billing line (millions of round-trips) 6. No per-document Commit() leaving entire run as one giant transaction Changes: - BillingLine.Table.al: add SK7 (Partner, ContractNo, ContractLineNo, DocType, DocNo) covering the GetBillingLineNo() and UsageDataBilling query patterns - SubscriptionLine.Table.al: add Key4 (Partner, ContractNo, NextBillingDate) covering the ProcessContractServiceCommitments filter pattern - UsageDataBilling.Table.al: add key2 (Partner, ContractNo, ContractLineNo, DocType, DocNo) enabling efficient Usage Based Billing document-save lookups - UsageDataGenericImport.Table.al: add key2 (UsageDataImportEntryNo, ProcessingStatus) enabling efficient status-filtered queries on import lines - BillingProposal.Codeunit.al: add progress dialog (BillingProposalProgressTxt) showing contract no., partner no., and processed/total count; guard all Message/Page.Run with if GuiAllowed to allow Job Queue execution - CreateBillingDocuments.Codeunit.al: - Wrap all Window.Open/Close/Update with if GuiAllowed - Wrap ProcessingFinishedMessage with if GuiAllowed - Add Commit() after each completed billing document in all four document-creation procedures (PerContract/PerCustomer for Sales; PerContract/PerVendor for Purchase) to create recovery points and reduce transaction size - Hoist GetBillingLineNo() call outside the UsageDataBilling FindSet() loop in both InsertSalesLineFromTempBillingLine and InsertPurchaseLineFromTempBillingLine; the result is constant within a given document line so calling it once is correct - Cache CustomerContract.Get() and VendorContract.Get() in CreateTempBillingLines using a last-seen contract no. variable; reduces O(n) Get() calls to O(distinct contracts) with zero behavioral change - Extend ProgressTxt label with a third field showing contracts-processed count Fixes microsoft#7690
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7690
Summary
Subscription Billing document creation is unscalable at 4M+ subscription lines due to missing indexes, crashes in Job Queue, an O(n2) query pattern, redundant database reads, and no per-document transaction boundary. This PR addresses all root causes identified in the issue.
Root Causes Fixed
1. Missing database indexes
BillingLine: Added keySK7on(Partner, Subscription Contract No., Subscription Contract Line No., Document Type, Document No.)coveringGetBillingLineNo()on every line insertionSubscriptionLine: Added keyKey4on(Partner, Subscription Contract No., Next Billing Date)coveringProcessContractServiceCommitments()UsageDataBilling: Added keykey2on(Partner, Subscription Contract No., Subscription Contract Line No., Document Type, Document No.)mirroring the billing line lookup patternUsageDataGenericImport: Added keykey2on(Usage Data Import Entry No., Processing Status)covering status-filtered scans during import2. Job Queue crash (GuiAllowed = false)
BillingProposal:ProposalWindow.Open/Update/Closenow guarded withif GuiAllowed. TheCount()call that feeds the progress dialog is also skipped whenGuiAllowed = false.CreateBillingDocuments:Window.Open/Update/Closealready had anAutomatedBillingguard but was missingGuiAllowedchecks. Both guards are now applied.ProcessingFinishedMessageexits early whennot GuiAllowed.3. Redundant GetBillingLineNo() calls inside inner loop
InsertSalesLineFromTempBillingLineandInsertPurchaseLineFromTempBillingLine,GetBillingLineNo()was called inside aUsageDataBilling.FindSet()loop with constant parameters. Hoisted outside the loop.4. Redundant contract Get() on every billing line
CreateTempBillingLinescalledCustomerContract.Get()andVendorContract.Get()for every billing line. Added cache variablesCachedCustomerContractNoandCachedVendorContractNoto skip the read when the contract has not changed.5. Single transaction for entire batch
CreateSalesDocumentsPerContract,CreatePurchaseDocumentsPerContract,CreateSalesDocumentsPerCustomer, andCreatePurchaseDocumentsPerVendoreach callCommit()after completing each document. This limits the transaction scope to one document at a time, preventing lock escalation and enabling partial-batch recovery.6. Progress visibility in BillingProposal
Dialogprogress window inCreateBillingProposalso users see per-contract progress during long proposal creation runs. Includes contract number, partner number, and a processed/total counter.Files Changed
src/Apps/W1/Subscription Billing/App/Billing/Tables/BillingLine.Table.al-- added SK7src/Apps/W1/Subscription Billing/App/Service Commitments/Tables/SubscriptionLine.Table.al-- added Key4src/Apps/W1/Subscription Billing/App/Usage Based Billing/Tables/UsageDataBilling.Table.al-- added key2src/Apps/W1/Subscription Billing/App/Usage Based Billing/Tables/UsageDataGenericImport.Table.al-- added key2src/Apps/W1/Subscription Billing/App/Billing/Codeunits/BillingProposal.Codeunit.al-- progress dialog + GuiAllowed guardssrc/Apps/W1/Subscription Billing/App/Billing/Codeunits/CreateBillingDocuments.Codeunit.al-- GuiAllowed guards, Commit() per document, hoisted GetBillingLineNo(), cached contract Get()How to Test