Skip to content

knowledge(performance): add trigger-only fields to API pages with AddLoadFields - #253

Open
Kilian Seizinger (pri-kise) wants to merge 3 commits into
microsoft:mainfrom
pri-kise:perf-addloadfields-on-api-pages
Open

Kilian Seizinger (pri-kise) wants to merge 3 commits into
microsoft:mainfrom
pri-kise:perf-addloadfields-on-api-pages

Conversation

@pri-kise

Copy link
Copy Markdown
Contributor

Mistake prevented

API and OData pages load only the fields bound to page controls (partial records applied by the platform). A common API pattern binds an API field to a global variable and fills it from Rec in OnAfterGetRecord, for example to convert a currency code or combine address fields. The Rec fields read there are not bound to any control. Each row's first access then triggers a JIT load (an implicit Get), and a concurrent change can make that load fail with "Inconsistent read of field(s)" or "JIT loading of field(s) ... failed".

The compiler and analyzers don't flag this, and the page returns correct data, so a reviewer or an authoring agent misses it. The cost only shows in telemetry and SQL load. We hit exactly this in partner API pages: telemetry reported "JIT loading of field(s): 'Post Code' failed for table: 'Contact'" until Rec.AddLoadFields was added in OnOpenPage.

The article also flags the tempting wrong fix: Rec.SetLoadFields in OnOpenPage. It replaces the platform's layout-based selection, so layout fields that aren't listed become JIT loads.

Evidence

Versions

bc-version: [17..]: partial records and AddLoadFields arrived in BC 2020 release wave 2.

Domain

performance, next to the report counterpart addloadfields-in-report-onpredataitem.md, which covers the same mechanic for report dataitems.

Checks

  • tools/Build-KnowledgeIndex.ps1: the article parses and is indexed.
  • tools/Test-ReviewFixtures.ps1: passed.
  • tools/Test-ReviewContract.ps1: passed.
  • validate_frontmatter.py was not run locally (no Python on this machine); CI will run it.

…LoadFields

API/OData pages load only fields bound to page controls. Fields that only
trigger code reads (typically OnAfterGetRecord filling a variable-bound API
field) are JIT-loaded per row. Recommend Rec.AddLoadFields in OnOpenPage,
as Microsoft's ExpenseAgent API pages do, and flag SetLoadFields there
because it replaces the platform's layout-based selection.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 3b403aaef038dcee94a3fd0a9f41023aafb382a7 with independent Sol and Opus assessments. The central additive-load pattern and pinned BCApps examples are useful. Three bounded corrections are needed before approval:

  1. Require a genuinely missing field, not just an unbound field and no OnOpenPage call. The Anti Pattern at .md:30 also matches unbound primary-key/SystemId/audit/filtered fields, which are already selected according to the AddLoadFields remarks. For example, filling a variable-backed API field from the unbound Customer primary key does not need another load selection. The article itself also allows OnFindRecord, but the detector accepts only OnOpenPage. Please exempt already-loaded fields and other supported effective selections. Likewise, flag SetLoadFields only when its resulting projection actually drops a needed field; a fieldless call loads all readable normal fields. Qualify the guaranteed one-database-round-trip-per-row wording: documented caching/enumerator reuse prevents inferring that count without runtime evidence.

  2. Register the new pair in changed-path evaluation. The performance override in evaluation/review-fixtures.json does not select this article, and there is no waiver. Running Test-ReviewFixtures with the actual three changed paths fails. The no-path run passes precisely because it does not exercise the new pair. Selecting the slug in a scratch manifest passes 294 prepared cases and retrieves the article naturally, so no special ranking or routing change is needed.

  3. Make both source and prepared samples valid at the advertised minimum. The Good page name is 31 characters and fails AL0305. The Bad name is 30 but becomes 31 after Bad-to-Eval neutralization, so both prepared cases exceed the limit. Shorten both name bases. With BC17/runtime6 retained, both pages also need DelayedInsert=true: after shortening, independent compiler controls still report AL0505 without it; the diagnostic dates the Editable=false relaxation to runtime7.2. Shortening and adding that property passes the runtime6 controls.

Compiler checks used AL18.1 with an explicit minimal test-only Customer table, not a real Base Application integration build. No API/JIT/SQL runtime behavior or model-scored evaluation was measured. Substantive GitHub workflows remain pending maintainer permission; I have not approved runs or merged anything.

…me-6 samples

- Exempt fields that are already selected (bound, primary key, SystemId,
  audit and filtered fields) and accept OnFindRecord as well as OnOpenPage.
- Flag SetLoadFields only when its projection drops a needed field; a
  fieldless call loads all normal fields.
- Drop the fixed one-round-trip-per-row claim; JIT cost depends on enumerator
  updates and caching.
- Register the article in the performance evaluation override.
- Shorten sample page names to stay within 30 characters after Bad/Good to
  Eval neutralization and add DelayedInsert = true for runtime 6.
@pri-kise

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review. All three points are addressed in a2a18fd (after merging current main):

  1. Effective selection. The Anti Pattern now requires a normal field that nothing selects: it is not bound, not always loaded (primary key, SystemId, audit fields, filtered fields, per the AddLoadFields remarks), and not added via AddLoadFields in OnOpenPage or OnFindRecord. SetLoadFields is flagged only when its projection drops a field that the layout or the triggers read; a fieldless SetLoadFields() is called out as not causing JIT loads. The fixed "one round trip per row" wording is gone. The article now refers to JIT loads and extra data-source access, and notes that enumerator updates and caching make the count depend on runtime evidence.
  2. Evaluation. The slug is added to the performance override in evaluation/review-fixtures.json. Test-ReviewFixtures.ps1 -ChangedPathsFile with the actual changed paths now passes (294 cases, 147/368 paired articles).
  3. Samples. The page names are now Perf API LoadFields Good/Bad (24 characters, also after the Eval neutralization), and both pages have DelayedInsert = true. I could not run an AL compile locally this time, so the AL0305/AL0505 fixes are not compiler-verified on my side.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved a2a18fd81f849c87924a7d46c94860a3488538d9 after independent Sol and Opus bounded rereviews. This supersedes my previous changes-requested review; all three requested-change groups are resolved.

The normative rule now requires a genuinely missing normal field, excludes automatically/effectively selected fields, allows OnFindRecord and complete or fieldless SetLoadFields selections, and no longer guarantees a SQL round trip per row. The actual performance manifest selects the pair exactly once and preserves main registrations; the real changed-path gate passes with 294 cases. Both source names and prepared Eval names fit the limit, and both pages retain DelayedInsert=true for the advertised minimum target.

Independent runtime-6.0-targeted compiler checks pass for canonical/prepared examples with an explicit test-only Customer schema; old/removal controls reproduce the original diagnostics. This is language/syntax compatibility evidence, not authentic Base Application integration, API/JIT/SQL runtime observation or model-scored precision. No merge-critical issue remains in the reviewed delta.

Substantive GitHub workflows still await maintainer permission and must run before merging. No workflow approval or merge was performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants