- performance/use-setloadfields-for-partial-records: Clarify that filter-only fields (SetRange/SetFilter) do not need to be listed in SetLoadFields — the DB resolves them via the index without hydrating the value into AL memory. - performance/avoid-calcfields-in-loops: Add explicit exception for OnAfterGetRecord and OnValidate triggers, which are platform-managed and not developer-authored loops. - performance/split-read-only-and-write-paths-to-avoid-locktable: Add ReadIsolation as the primary recommendation for read-only paths; LockTable reserved for confirmed write paths only. - performance/prefer-direct-record-over-recordref: Scope the finding to hot unbounded loops (10k+ rows) over ledger-entry-scale tables; RecordRef in bounded/admin/setup contexts is not a concern. - upgrade/enum-changes-must-be-additive-at-the-end: Replace direct ObsoleteState = Removed guidance with the two-stage workflow (Pending first, Removed later); reference use-obsolete-pending-before-removed. - upgrade/use-datatransfer-for-large-dataset-initialization: Add the >300,000 records threshold as the concrete trigger for requiring DataTransfer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1.9 KiB
| bc-version | domain | keywords | technologies | countries | application-area | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
performance |
|
|
|
|
Split read-only and write paths so LockTable runs only when needed
Description
LockTable takes an exclusive write lock on the affected table for the remainder of the transaction. In a helper that is called from many read-only sites and a few write sites, placing LockTable unconditionally at the top serializes every reader on every other reader's lock — the helper becomes a system-wide contention point. The correct shape is a conditional structure: try the read-only path first, and only fall through to LockTable when the code genuinely needs to modify the table.
Best Practice
For paths that are read-only, prefer ReadIsolation over LockTable. Setting Rec.ReadIsolation := IsolationLevel::ReadCommitted on a record variable gives fine-grained, per-instance control over the isolation level without taking an update lock on the table for the rest of the transaction. Use LockTable only for paths that genuinely write to the table.
For helpers that may or may not modify records, factor the code so readers return immediately without a lock and only writers reach the LockTable call. A common pattern: attempt Rec.Get() first; if it returns the row, exit with the value; otherwise LockTable and proceed with the Insert. Document the pattern in a comment on the helper so callers understand why the LockTable is inside a branch.
See sample: split-read-only-and-write-paths-to-avoid-locktable.good.al.
Anti Pattern
A GetOrCreate helper that unconditionally calls Rec.LockTable() at the top, then Gets the row, then returns it. Every reader now blocks every other reader even though none of them intend to write. Under load the helper becomes the dominant bottleneck.
See sample: split-read-only-and-write-paths-to-avoid-locktable.bad.al.