Skip to content

Commit 1f81225

Browse files
authored
Merge pull request #3673 from ningmingxiao/nerd_ps
fix nerdctl ps slow on heavy IO system by using goroutine
2 parents 47f31ff + 788fc0c commit 1f81225

File tree

1 file changed

+36
-10
lines changed

1 file changed

+36
-10
lines changed

pkg/cmd/container/list.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"sort"
2424
"strings"
25+
"sync"
2526
"time"
2627

2728
containerd "github.com/containerd/containerd/v2/client"
@@ -40,11 +41,11 @@ import (
4041

4142
// List prints containers according to `options`.
4243
func List(ctx context.Context, client *containerd.Client, options types.ContainerListOptions) ([]ListItem, error) {
43-
containers, err := filterContainers(ctx, client, options.Filters, options.LastN, options.All)
44+
containers, cMap, err := filterContainers(ctx, client, options.Filters, options.LastN, options.All)
4445
if err != nil {
4546
return nil, err
4647
}
47-
return prepareContainers(ctx, client, containers, options)
48+
return prepareContainers(ctx, client, containers, cMap, options)
4849
}
4950

5051
// filterContainers returns containers matching the filters.
@@ -53,14 +54,14 @@ func List(ctx context.Context, client *containerd.Client, options types.Containe
5354
// - all means showing all containers (default shows just running).
5455
// - lastN means only showing n last created containers (includes all states). Non-positive values are ignored.
5556
// In other words, if lastN is positive, all will be set to true.
56-
func filterContainers(ctx context.Context, client *containerd.Client, filters []string, lastN int, all bool) ([]containerd.Container, error) {
57+
func filterContainers(ctx context.Context, client *containerd.Client, filters []string, lastN int, all bool) ([]containerd.Container, map[string]string, error) {
5758
containers, err := client.Containers(ctx)
5859
if err != nil {
59-
return nil, err
60+
return nil, nil, err
6061
}
6162
filterCtx, err := foldContainerFilters(ctx, containers, filters)
6263
if err != nil {
63-
return nil, err
64+
return nil, nil, err
6465
}
6566
containers = filterCtx.MatchesFilters(ctx)
6667

@@ -77,18 +78,37 @@ func filterContainers(ctx context.Context, client *containerd.Client, filters []
7778
}
7879
}
7980

81+
var wg sync.WaitGroup
82+
statusPerContainer := make(map[string]string)
83+
var mu sync.Mutex
84+
// formatter.ContainerStatus(ctx, c) is time consuming so we do it in goroutines and return the container's id with status as a map.
85+
// prepareContainers func will use this map to avoid call formatter.ContainerStatus again.
86+
for _, c := range containers {
87+
if c.ID() == "" {
88+
return nil, nil, fmt.Errorf("container id is nill")
89+
}
90+
wg.Add(1)
91+
go func(ctx context.Context, c containerd.Container) {
92+
defer wg.Done()
93+
cStatus := formatter.ContainerStatus(ctx, c)
94+
mu.Lock()
95+
statusPerContainer[c.ID()] = cStatus
96+
mu.Unlock()
97+
}(ctx, c)
98+
}
99+
wg.Wait()
80100
if all || filterCtx.all {
81-
return containers, nil
101+
return containers, statusPerContainer, nil
82102
}
83103

84104
var upContainers []containerd.Container
85105
for _, c := range containers {
86-
cStatus := formatter.ContainerStatus(ctx, c)
106+
cStatus := statusPerContainer[c.ID()]
87107
if strings.HasPrefix(cStatus, "Up") {
88108
upContainers = append(upContainers, c)
89109
}
90110
}
91-
return upContainers, nil
111+
return upContainers, statusPerContainer, nil
92112
}
93113

94114
type ListItem struct {
@@ -112,7 +132,7 @@ func (x *ListItem) Label(s string) string {
112132
return x.LabelsMap[s]
113133
}
114134

115-
func prepareContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, options types.ContainerListOptions) ([]ListItem, error) {
135+
func prepareContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, statusPerContainer map[string]string, options types.ContainerListOptions) ([]ListItem, error) {
116136
listItems := make([]ListItem, len(containers))
117137
snapshottersCache := map[string]snapshots.Snapshotter{}
118138
for i, c := range containers {
@@ -136,6 +156,12 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
136156
if options.Truncate && len(id) > 12 {
137157
id = id[:12]
138158
}
159+
var status string
160+
if s, ok := statusPerContainer[c.ID()]; ok {
161+
status = s
162+
} else {
163+
return nil, fmt.Errorf("can't get container %s status", c.ID())
164+
}
139165
li := ListItem{
140166
Command: formatter.InspectContainerCommand(spec, options.Truncate, true),
141167
CreatedAt: info.CreatedAt,
@@ -144,7 +170,7 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
144170
Platform: info.Labels[labels.Platform],
145171
Names: containerutil.GetContainerName(info.Labels),
146172
Ports: formatter.FormatPorts(info.Labels),
147-
Status: formatter.ContainerStatus(ctx, c),
173+
Status: status,
148174
Runtime: info.Runtime.Name,
149175
Labels: formatter.FormatLabels(info.Labels),
150176
LabelsMap: info.Labels,

0 commit comments

Comments
 (0)