Skip to content

Optimize picklist label resolution with bulk PicklistAttributeMetadata fetch#154

Merged
abelmilash-msft merged 6 commits intomainfrom
users/abelmilash/optionset_fix
Apr 8, 2026
Merged

Optimize picklist label resolution with bulk PicklistAttributeMetadata fetch#154
abelmilash-msft merged 6 commits intomainfrom
users/abelmilash/optionset_fix

Conversation

@abelmilash-msft
Copy link
Copy Markdown
Contributor

@abelmilash-msft abelmilash-msft commented Mar 25, 2026

Summary

Reduces API calls during picklist label-to-integer resolution by fetching all picklist attributes and their options for the entire table in a single API call using the PicklistAttributeMetadata cast, instead of checking each attribute individually. Results are cached with a 1-hour TTL.

Changes

src/PowerPlatform/Dataverse/data/_odata.py

  • Add _bulk_fetch_picklists() — single API call to fetch all picklist attributes and their options for a table
  • Add _request_metadata_with_retry() — exponential backoff on transient metadata errors
  • Simplify _convert_labels_to_ints() — calls _bulk_fetch_picklists then resolves labels from cache

tests/unit/data/test_odata_internal.py

  • Rewrite TestPicklistLabelResolution class with 50 unit tests covering _bulk_fetch_picklists, _request_metadata_with_retry, _convert_labels_to_ints, integration through _create/_update/_upsert, and edge cases

examples/advanced/walkthrough.py

  • Add picklist label update test to Section 10 (verifies both create and update with string labels)

Performance impact

Cold cache API calls reduced from n + p to always 1, where p = picklist fields, n = string fields.

Picklist Columns Before Calls Before Time After Calls After Time Speedup
1 2 0.6s 1 0.3s 2x
10 11 3.3s 1 0.4s 9x
100 101 34s 1 0.6s 55x
250 251 79s 1 1.2s 64x
400 401 119s 1 1.3s 92x

Repeat operations use a 1-hour TTL cache (0 API calls, <5ms).

Testing

  • 660 unit tests passing
  • Performance benchmarks verified against live Dataverse environment (5 runs each)

@abelmilash-msft abelmilash-msft requested a review from a team as a code owner March 25, 2026 07:18
Copilot AI review requested due to automatic review settings March 25, 2026 07:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Optimizes picklist label-to-integer resolution by reducing metadata API calls: attribute types are batch-checked once via Microsoft.Dynamics.CRM.In, and OptionSet metadata is fetched only for confirmed picklist attributes.

Changes:

  • Added _request_metadata_with_retry() and _check_attribute_types() to support batched attribute type detection with retry-on-404 behavior.
  • Simplified _optionset_map() to skip per-attribute type probing and only fetch picklist OptionSet metadata.
  • Refactored _convert_labels_to_ints() to batch type-check uncached string fields and resolve only confirmed picklists; added extensive unit tests and updated walkthrough.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/PowerPlatform/Dataverse/data/_odata.py Adds batch attribute-type checking + shared metadata retry helper; refactors picklist label resolution flow to reduce API calls.
tests/unit/data/test_odata_internal.py Adds comprehensive unit tests for the new batching/retry behavior and integration via _create/_update/_upsert.
examples/advanced/walkthrough.py Extends walkthrough to verify picklist label updates using string labels.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@abelmilash-msft abelmilash-msft force-pushed the users/abelmilash/optionset_fix branch 3 times, most recently from 0101f03 to 2ae992a Compare March 27, 2026 06:28
@abelmilash-msft abelmilash-msft changed the title Optimize picklist label resolution with batch attribute type-check Optimize picklist label resolution with bulk PicklistAttributeMetadata fetch Mar 27, 2026
saurabhrb
saurabhrb previously approved these changes Mar 31, 2026
sagebree
sagebree previously approved these changes Apr 8, 2026
…gnature change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@abelmilash-msft abelmilash-msft merged commit 78cd852 into main Apr 8, 2026
9 checks passed
@abelmilash-msft abelmilash-msft deleted the users/abelmilash/optionset_fix branch April 8, 2026 21:38
maksii added a commit to maksii/PowerPlatform-DataverseClient-Python that referenced this pull request Apr 10, 2026
Incorporate latest changes from microsoft/PowerPlatform-DataverseClient-Python:
- Batch API with changeset, upsert, and DataFrame integration (microsoft#129)
- Optimize picklist label resolution with bulk fetch (microsoft#154)
- Add memo/multiline column type support (microsoft#155)
- Add unit test coverage and CI coverage reporting (microsoft#158)

Made-with: Cursor
abelmilash-msft added a commit that referenced this pull request Apr 10, 2026
Release changelog for v0.1.0b8.

## Changes included in this release

### Added
- Batch API: `client.batch` namespace for deferred-execution batch
operations (#129)
- Batch DataFrame integration: `client.batch.dataframe` namespace (#129)
- `client.records.upsert()` and `client.batch.records.upsert()` with
alternate-key support (#129)
- QueryBuilder: `client.query.builder()` with fluent API and composable
filter expressions (#118)
- Memo/multiline column type support in `client.tables.create()` and
`client.tables.add_columns()` (#155)

### Changed
- Picklist label resolution now uses a single bulk API call per table
with 1-hour TTL cache (#154)

### Fixed
- `client.query.sql()` now paginates and returns all rows instead of
truncating at 5,000 (#157)
- Alternate key fields no longer merged into `UpsertMultiple` request
body (#129)
- Docstring type annotations corrected for Microsoft Learn compatibility
(#153)

---------

Co-authored-by: Abel Milash <abelmilash@microsoft.com>
abelmilash-msft added a commit that referenced this pull request Apr 11, 2026
Release changelog for v0.1.0b8.

## Changes included in this release

### Added
- Batch API: `client.batch` namespace for deferred-execution batch
operations (#129)
- Batch DataFrame integration: `client.batch.dataframe` namespace (#129)
- `client.records.upsert()` and `client.batch.records.upsert()` with
alternate-key support (#129)
- QueryBuilder: `client.query.builder()` with fluent API and composable
filter expressions (#118)
- Memo/multiline column type support in `client.tables.create()` and
`client.tables.add_columns()` (#155)

### Changed
- Picklist label resolution now uses a single bulk API call per table
with 1-hour TTL cache (#154)

### Fixed
- `client.query.sql()` now paginates and returns all rows instead of
truncating at 5,000 (#157)
- Alternate key fields no longer merged into `UpsertMultiple` request
body (#129)
- Docstring type annotations corrected for Microsoft Learn compatibility
(#153)

---------

Co-authored-by: Abel Milash <abelmilash@microsoft.com>
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.

4 participants