Skip to content

Commit

Permalink
Merge pull request #608 from sapcc/liquid-ironic-improve-memory-usage
Browse files Browse the repository at this point in the history
liquid-ironic: improve peak memory usage
  • Loading branch information
majewsky authored Nov 22, 2024
2 parents ed48326 + ac25939 commit 9bdd8fb
Showing 1 changed file with 34 additions and 11 deletions.
45 changes: 34 additions & 11 deletions internal/liquids/ironic/capacity.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,35 @@ func (l *Logic) ScanCapacity(ctx context.Context, req liquid.ServiceCapacityRequ
}

// enumerate Ironic nodes and sort by resource class (which should contain the flavor name)
allPages, err := ListNodesDetail(l.IronicV1).AllPages(ctx)
if err != nil {
return liquid.ServiceCapacityReport{}, err
}
var allNodes []Node
err = ExtractNodesInto(allPages, &allNodes)
//
// NOTE: In most cases, we pull AllPages() at once when dealing with a paginated API.
// However, baremetal nodes have an extremely high number of attributes, most of which we don't care about.
// Holding them all in memory at once in the AllPages result object creates a very big spike in memory usage.
//
// To avoid this, this implementation uses EachPage() to pull in only 100 nodes at a time,
// and then parses into our own reduced representation in `type Node` which can be stored much more efficiently.
// Profiling on a cluster with 1850 Ironic nodes showed the following peak memory usage levels:
//
// - AllPages: 109.27MB
// - EachPage (limit = 1000): 94.11MB
// - EachPage (limit = 100): 20.21MB
//
nodesByFlavorName := make(map[string][]Node)
opts := &nodes.ListOpts{Limit: 100}
err = ListNodesDetail(l.IronicV1, opts).EachPage(ctx, func(ctx context.Context, page pagination.Page) (bool, error) {
var nodes []Node
err = ExtractNodesInto(page, &nodes)
if err != nil {
return false, err
}
for _, node := range nodes {
nodesByFlavorName[node.ResourceClass] = append(nodesByFlavorName[node.ResourceClass], node)
}
return true, nil
})
if err != nil {
return liquid.ServiceCapacityReport{}, err
}
nodesByFlavorName := make(map[string][]Node)
for _, node := range allNodes {
nodesByFlavorName[node.ResourceClass] = append(nodesByFlavorName[node.ResourceClass], node)
}

// build result
var (
Expand Down Expand Up @@ -230,8 +246,15 @@ func (n Node) StableProvisionState() string {

// ListNodesDetail is like `nodes.ListDetail(client, nil)`,
// but works around <https://github.com/gophercloud/gophercloud/issues/2431>.
func ListNodesDetail(client *gophercloud.ServiceClient) pagination.Pager {
func ListNodesDetail(client *gophercloud.ServiceClient, opts *nodes.ListOpts) pagination.Pager {
url := client.ServiceURL("nodes", "detail")
if opts != nil {
query, err := opts.ToNodeListDetailQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
return nodePage{nodes.NodePage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}}}
})
Expand Down

0 comments on commit 9bdd8fb

Please sign in to comment.