Skip to content

compute: stop re-decoding server list pages in IsEmpty and NextPageURL - #4044

Merged
mandre merged 4 commits into
gophercloud:mainfrom
blacksoxx:servers-list-single-unmarshal
Nov 12, 2026
Merged

mandre merged 4 commits into
gophercloud:mainfrom
blacksoxx:servers-list-single-unmarshal

Conversation

@blacksoxx

@blacksoxx blacksoxx commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3867

Links to the line numbers/files in the OpenStack source code that support the
code in this PR:

https://github.com/openstack/nova/blob/stable/2025.1/nova/api/openstack/compute/views/servers.py#L581-L587
https://github.com/openstack/nova/blob/stable/2025.1/nova/api/openstack/common.py#L416-L446

ServerPage.IsEmpty and ServerPage.NextPageURL both went through ExtractInto, which marshals the body that pagination.PageResultFrom has already decoded and parses it again. With an EachPage handler calling ExtractServers, every page was serialised three times and parsed four times. They now read servers and servers_links from the decoded map and fall back to ExtractInto when the body is not that map or when servers or servers_links are not lists of objects, so those cases still return the same errors and hand built pages still work. What changes is the validation of the fields inside each server: IsEmpty no longer decodes them, so a type mismatch there is reported by ExtractServers instead, one step later for EachPage and after the merge for AllPages. The generic decode in PageResultFrom stays, AllPages and GetBody depend on it.

On a synthetic page of 500 servers (2.2 MB, 4 KiB user_data each):

before after
IsEmpty 22 ms 8 ns
NextPageURL 3.9 ms 37 ns
one EachPage iteration 27.4 ms, 65 MB 12.5 ms, 32 MB

The remainder is ExtractServers itself, which still marshals the body once; halving that needs changes in the root package and is left out here.

Adds a two page List fixture, which was not covered before, and a test that counts body re-encodes. The same pattern applies to most other packages; the servers code can be lifted into a pagination helper if you want it adopted more widely.

ServerPage.IsEmpty and ServerPage.NextPageURL both went through
ExtractInto, which re-encodes the page body that
pagination.PageResultFrom has already decoded and then parses it again.
Together with the ExtractServers call in the EachPage handler, every
page returned by servers.List was serialised three times and parsed four
times, which gets expensive with many servers or large user_data.

Read the servers list and servers_links from the decoded body instead
and only fall back to ExtractInto when the body is not the decoded map.
ExtractServers is now the only full decode per page.

Add a two page List fixture, which was not covered before, and a test
that counts body re-encodes.

Fix: gophercloud#3867
Signed-off-by: Youssef Omar Bouden <[email protected]>
@github-actions github-actions Bot added edit:compute This PR updates compute code semver:patch No API change backport-v2 This PR will be backported to v2 labels Oct 11, 2026
@coveralls

coveralls commented Oct 12, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 64.128% (+0.3%) from 63.787% — blacksoxx:servers-list-single-unmarshal into gophercloud:main

Comment thread openstack/compute/v2/servers/results.go Resolved
Comment thread openstack/compute/v2/servers/results.go Resolved
Comment thread openstack/compute/v2/servers/results.go Resolved
IsEmpty and NextPageURL returned decoded values even when the page
carried an error, and NextPageURL dropped servers_links values that
were not []any, such as a hand built []gophercloud.Link. Both now
return r.Err first, and the fast paths only answer when the decoded
value has the expected shape; anything else goes through ExtractInto
as before so conversions and decoding errors are preserved.

Signed-off-by: Youssef Omar Bouden <[email protected]>
@github-actions github-actions Bot added semver:minor Backwards-compatible change and removed semver:patch No API change labels Oct 21, 2026
Comment thread openstack/compute/v2/servers/results.go Resolved
@github-actions github-actions Bot added semver:major Breaking change and removed semver:minor Backwards-compatible change backport-v2 This PR will be backported to v2 labels Nov 12, 2026
@mandre
mandre enabled auto-merge November 12, 2026 14:28
@mandre mandre added semver:patch No API change and removed semver:major Breaking change labels Nov 12, 2026
@mandre
mandre added this pull request to the merge queue Nov 12, 2026
Merged via the queue into gophercloud:main with commit 519dcf8 Nov 12, 2026
77 checks passed
@mandre mandre added the backport-v2 This PR will be backported to v2 label Nov 12, 2026
@github-actions

Copy link
Copy Markdown

Failed to backport PR to v2 branch. See logs for details.

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

Labels

backport-v2 This PR will be backported to v2 edit:compute This PR updates compute code semver:patch No API change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

servers.List() pagination is extremely inefficient

3 participants