Optimize docker ps
when name/id filters in use
When a partial ID or name is used in `docker ps` filters, today the entire list of containers is walked even though there are shorter paths to acquiring the subset of containers that match the ID or name. Also, container's locks are used during this walk, causing increased lock contention on a busy daemon. Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com>
This commit is contained in:
parent
01409bf069
commit
8e4a451448
1 changed files with 56 additions and 1 deletions
|
@ -90,6 +90,56 @@ func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.C
|
|||
return daemon.reduceContainers(config, daemon.transformContainer)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Container {
|
||||
idSearch := false
|
||||
names := ctx.filters.Get("name")
|
||||
ids := ctx.filters.Get("id")
|
||||
if len(names)+len(ids) == 0 {
|
||||
// if name or ID filters are not in use, return to
|
||||
// standard behavior of walking the entire container
|
||||
// list from the daemon's in-memory store
|
||||
return daemon.List()
|
||||
}
|
||||
|
||||
// idSearch will determine if we limit name matching to the IDs
|
||||
// matched from any IDs which were specified as filters
|
||||
if len(ids) > 0 {
|
||||
idSearch = true
|
||||
}
|
||||
|
||||
matches := make(map[string]bool)
|
||||
// find ID matches; errors represent "not found" and can be ignored
|
||||
for _, id := range ids {
|
||||
if fullID, err := daemon.idIndex.Get(id); err == nil {
|
||||
matches[fullID] = true
|
||||
}
|
||||
}
|
||||
|
||||
// look for name matches; if ID filtering was used, then limit the
|
||||
// search space to the matches map only; errors represent "not found"
|
||||
// and can be ignored
|
||||
if len(names) > 0 {
|
||||
for id, idNames := range ctx.names {
|
||||
// if ID filters were used and no matches on that ID were
|
||||
// found, continue to next ID in the list
|
||||
if idSearch && !matches[id] {
|
||||
continue
|
||||
}
|
||||
for _, eachName := range idNames {
|
||||
if ctx.filters.Match("name", eachName) {
|
||||
matches[id] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cntrs := make([]*container.Container, 0, len(matches))
|
||||
for id := range matches {
|
||||
cntrs = append(cntrs, daemon.containers.Get(id))
|
||||
}
|
||||
return cntrs
|
||||
}
|
||||
|
||||
// reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
|
||||
func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
|
||||
containers := []*types.Container{}
|
||||
|
@ -99,7 +149,12 @@ func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reduc
|
|||
return nil, err
|
||||
}
|
||||
|
||||
for _, container := range daemon.List() {
|
||||
// fastpath to only look at a subset of containers if specific name
|
||||
// or ID matches were provided by the user--otherwise we potentially
|
||||
// end up locking and querying many more containers than intended
|
||||
containerList := daemon.filterByNameIDMatches(ctx)
|
||||
|
||||
for _, container := range containerList {
|
||||
t, err := daemon.reducePsContainer(container, ctx, reducer)
|
||||
if err != nil {
|
||||
if err != errStopIteration {
|
||||
|
|
Loading…
Add table
Reference in a new issue