Repository navigation
fix(hubs): paginate Azure Resource Graph queries past 1,000 rows - #2330
Michael Flanakin (flanakin) wants to merge 3 commits into
Conversation
ARG caps a single response at 1,000 rows and returns a $skipToken for the rest, but that token must be resubmitted in the POST body, which the ADF REST connector's paginationRules can't inject into (only URL, query string, or headers). The ExecuteQuery pipeline now loops until Resource Graph stops returning a token, writing each page to its own Parquet file. Fixes #2320 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Roland Krummenacher (RolandKrummenacher)
left a comment
There was a problem hiding this comment.
Reviewed the pagination rework end to end. The Until shape is right and the diagnosis matches what I see in the module.
Things I checked that are fine, so you don't have to re-derive them: the _{page} suffix doesn't break ADX table routing (the table comes from the folder path, not the file name) or the drop-extents cleanup (per-file drop-by: tags plus a per-run ingestionId means no stale pages and no upgrade hazard); manifest gating means a mid-loop failure can't half-ingest; and the request-body JSON escaping and the zero-row exit both behave the same as the old | count path.
Five things below. The first two are the ones I'd want resolved before this merges — the Web activity payload limit could turn a truncation bug into a hard failure on the very dataset in the issue, and ARG paging without a sort can duplicate and drop rows.
Roland Krummenacher (RolandKrummenacher)
left a comment
There was a problem hiding this comment.
Heads-up: the five "Implemented" replies (Sep 21) aren't on the branch. xe/issue-2320-ef877f is still at bed630f from Sep 16 with no push since, so the commit may be sitting unpushed in a local worktree. Could you push it? I'll re-review then.
Threads 1 (output limit) and 3 (early loop exit) are the blockers, since both can silently drop data.
For the sort fix in thread 2: this module is generic, so a hardcoded order by x_RecommendationId would break future ARG queries without that column. Can the sort key come from the query config?
The PR also conflicts with dev (docs-mslearn/toolkit/changelog.md only); please merge origin/dev.
…, timeout, retry, path replace) Merge origin/dev and apply review feedback on ARG pagination. Co-Authored-By: RolandKrummenacher <RolandKrummenacher@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Roland Krummenacher (RolandKrummenacher)
left a comment
There was a problem hiding this comment.
Re-review — PR #2330 (head b9b7805)
All five review fixes are in b9b7805, and I checked each one in app.bicep:
$top500 is sent on every page, and$skipTokenonly when there is one.| order by x_RecommendationId asccomes before the source columns are added. Every shipped query inRecommendations/queriesreturnsx_RecommendationId, and none usestake,limit, orsample, so Resource Graph keeps returning a$skipToken.hasMorePagesdepends only on the token, andhasResultsonly controls the write. An empty page that still has a token keeps the loop going without writing a file.- The loop timeout is 6h, and
Write ARG Pageretries twice. A retry overwrites the same page file, so it doesn't create duplicates. replace(ingestionPath, '.parquet', …)replaces thesubstringarithmetic.
The module builds cleanly. Approving.
Documentation follow-ups (non-blocking)
- Engine README:
AzureResourceGraph/README.mdstill describes 1,000-row responses. Please mention the 500-row$toppage size and the forcedorder by x_RecommendationId. - Sort column is fixed: The engine is a generic
ExecuteQueryimplementation, but it now requiresx_RecommendationIdin every query result. That's fine today, because custom queries must use theRecommendationsdataset andx_RecommendationIdis a required column. A query for any other dataset would fail, so the README should say this, or the sort column could become a parameter later. - Custom query tips: The pipeline doesn't check
resultTruncated. If a custom query usestake,limit, orsample, Resource Graph returns no$skipToken, and only the first page is written without a warning. A one-line tip in Add custom recommendations would cover it.
Also note that the PR deployment jobs were skipped on this head, so CI hasn't run the paging loop end to end.
🤖 [AI]
Summary
$skipTokenwhen more results exist, but thequeries_ResourceGraph_ExecuteQuerypipeline ran a single Copy activity with no pagination, silently truncating larger result sets (for exampleRecommendations-Microsoft-AdvisorCost).paginationRulescan only inject a continuation value into a URL, query string, or header — not a POST body — so it can't express ARG's$skipTokencontinuation on its own.Untilloop: each iteration runs the query via a Web activity to check for results and read$skipToken, then re-runs the same page via Copy (to keep schema mapping/translator support) and writes it to its own_{page}.parquetfile. The loop stops once ARG stops returning a token.Test plan
bicep build src/templates/finops-hub/main.bicep --stdoutcompiles cleanlyInvoke-Pester -Path ./src/powershell/Tests/Unit/HubsIngestionQueries.Tests.ps1— 192/192 passedqueries_ExecuteETLagainst a tenant with >1,000 Advisor cost recommendations, confirm multiple_{page}.parquetfiles land in ingestion storage and all rows reach the destination tableFixes #2320
🤖 Generated with Claude Code