Skip to content

Commit b629627

Browse files
committed
fix nerdctl ps slow on heavy IO system by using goroutine
Signed-off-by: ningmingxiao <[email protected]>
1 parent 8b814ca commit b629627

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,36 @@ func filterContainers(ctx context.Context, client *containerd.Client, filters []
7778
}
7879
}
7980

81+
var wg sync.WaitGroup
82+
cMap := 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 will this map to avoid call formatter.ContainerStatus again.
86+
for _, c := range containers {
87+
wg.Add(1)
88+
go func(ctx context.Context, c containerd.Container) {
89+
defer wg.Done()
90+
cStatus := formatter.ContainerStatus(ctx, c)
91+
mu.Lock()
92+
if c.ID() != "" {
93+
cMap[c.ID()] = cStatus
94+
}
95+
mu.Unlock()
96+
}(ctx, c)
97+
}
98+
wg.Wait()
8099
if all || filterCtx.all {
81-
return containers, nil
100+
return containers, cMap, nil
82101
}
83102

84103
var upContainers []containerd.Container
85104
for _, c := range containers {
86-
cStatus := formatter.ContainerStatus(ctx, c)
105+
cStatus := cMap[c.ID()]
87106
if strings.HasPrefix(cStatus, "Up") {
88107
upContainers = append(upContainers, c)
89108
}
90109
}
91-
return upContainers, nil
110+
return upContainers, cMap, nil
92111
}
93112

94113
type ListItem struct {
@@ -112,7 +131,7 @@ func (x *ListItem) Label(s string) string {
112131
return x.LabelsMap[s]
113132
}
114133

115-
func prepareContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, options types.ContainerListOptions) ([]ListItem, error) {
134+
func prepareContainers(ctx context.Context, client *containerd.Client, containers []containerd.Container, cMap map[string]string, options types.ContainerListOptions) ([]ListItem, error) {
116135
listItems := make([]ListItem, len(containers))
117136
snapshottersCache := map[string]snapshots.Snapshotter{}
118137
for i, c := range containers {
@@ -136,6 +155,13 @@ func prepareContainers(ctx context.Context, client *containerd.Client, container
136155
if options.Truncate && len(id) > 12 {
137156
id = id[:12]
138157
}
158+
var status string
159+
s, ok := cMap[c.ID()]
160+
if 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)