Prevent race conditions when generating archives

This commit is contained in:
Dane Everitt 2020-07-16 19:56:53 -07:00
parent b2d34cf8e7
commit 7e1b7e7f36
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
3 changed files with 33 additions and 8 deletions

View File

@ -21,6 +21,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
) )
@ -510,17 +511,19 @@ func (d *DockerEnvironment) EnableResourcePolling() error {
return return
} }
s.Resources.Lock()
s.Resources.CpuAbsolute = s.Resources.CalculateAbsoluteCpu(&v.PreCPUStats, &v.CPUStats) s.Resources.CpuAbsolute = s.Resources.CalculateAbsoluteCpu(&v.PreCPUStats, &v.CPUStats)
s.Resources.Memory = s.Resources.CalculateDockerMemory(v.MemoryStats) s.Resources.Memory = s.Resources.CalculateDockerMemory(v.MemoryStats)
s.Resources.MemoryLimit = v.MemoryStats.Limit s.Resources.MemoryLimit = v.MemoryStats.Limit
s.Resources.Unlock()
// Why you ask? This already has the logic for caching disk space in use and then // Why you ask? This already has the logic for caching disk space in use and then
// also handles pushing that value to the resources object automatically. // also handles pushing that value to the resources object automatically.
s.Filesystem.HasSpaceAvailable() s.Filesystem.HasSpaceAvailable()
for _, nw := range v.Networks { for _, nw := range v.Networks {
s.Resources.Network.RxBytes += nw.RxBytes atomic.AddUint64(&s.Resources.Network.RxBytes, nw.RxBytes)
s.Resources.Network.TxBytes += nw.TxBytes atomic.AddUint64(&s.Resources.Network.TxBytes, nw.TxBytes)
} }
b, _ := json.Marshal(s.Resources) b, _ := json.Marshal(s.Resources)
@ -539,10 +542,7 @@ func (d *DockerEnvironment) DisableResourcePolling() error {
err := d.stats.Close() err := d.stats.Close()
d.Server.Resources.CpuAbsolute = 0 d.Server.Resources.Empty()
d.Server.Resources.Memory = 0
d.Server.Resources.Network.TxBytes = 0
d.Server.Resources.Network.RxBytes = 0
return errors.WithStack(err) return errors.WithStack(err)
} }

View File

@ -29,10 +29,9 @@ import (
var InvalidPathResolution = errors.New("invalid path resolution") var InvalidPathResolution = errors.New("invalid path resolution")
type Filesystem struct { type Filesystem struct {
// The server object associated with this Filesystem.
Server *Server Server *Server
Configuration *config.SystemConfiguration Configuration *config.SystemConfiguration
cacheDiskMu sync.Mutex
} }
// Returns the root path that contains all of a server's data. // Returns the root path that contains all of a server's data.
@ -171,7 +170,9 @@ func (fs *Filesystem) HasSpaceAvailable() bool {
// Determine if their folder size, in bytes, is smaller than the amount of space they've // Determine if their folder size, in bytes, is smaller than the amount of space they've
// been allocated. // been allocated.
fs.Server.Resources.Lock()
fs.Server.Resources.Disk = size fs.Server.Resources.Disk = size
fs.Server.Resources.Unlock()
// If space is -1 or 0 just return true, means they're allowed unlimited. // If space is -1 or 0 just return true, means they're allowed unlimited.
// //
@ -190,6 +191,15 @@ func (fs *Filesystem) HasSpaceAvailable() bool {
// excessive IO usage. We will only walk the filesystem and determine the size of the directory if there // excessive IO usage. We will only walk the filesystem and determine the size of the directory if there
// is no longer a cached value. // is no longer a cached value.
func (fs *Filesystem) getCachedDiskUsage() (int64, error) { func (fs *Filesystem) getCachedDiskUsage() (int64, error) {
// Obtain an exclusive lock on this process so that we don't unintentionally run it at the same
// time as another running process. Once the lock is available it'll read from the cache for the
// second call rather than hitting the disk in parallel.
//
// This effectively the same speed as running this call in parallel since this cache will return
// instantly on the second call.
fs.cacheDiskMu.Lock()
defer fs.cacheDiskMu.Unlock()
if x, exists := fs.Server.Cache.Get("disk_used"); exists { if x, exists := fs.Server.Cache.Get("disk_used"); exists {
return x.(int64), nil return x.(int64), nil
} }

View File

@ -3,12 +3,15 @@ package server
import ( import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"math" "math"
"sync"
) )
// Defines the current resource usage for a given server instance. If a server is offline you // Defines the current resource usage for a given server instance. If a server is offline you
// should obviously expect memory and CPU usage to be 0. However, disk will always be returned // should obviously expect memory and CPU usage to be 0. However, disk will always be returned
// since that is not dependent on the server being running to collect that data. // since that is not dependent on the server being running to collect that data.
type ResourceUsage struct { type ResourceUsage struct {
sync.RWMutex
// The total amount of memory, in bytes, that this server instance is consuming. This is // The total amount of memory, in bytes, that this server instance is consuming. This is
// calculated slightly differently than just using the raw Memory field that the stats // calculated slightly differently than just using the raw Memory field that the stats
// return from the container, so please check the code setting this value for how that // return from the container, so please check the code setting this value for how that
@ -31,6 +34,18 @@ type ResourceUsage struct {
} `json:"network"` } `json:"network"`
} }
// Resets the usages values to zero, used when a server is stopped to ensure we don't hold
// onto any values incorrectly.
func (ru *ResourceUsage) Empty() {
ru.Lock()
defer ru.Unlock()
ru.Memory = 0
ru.CpuAbsolute = 0
ru.Network.TxBytes = 0
ru.Network.RxBytes = 0
}
// The "docker stats" CLI call does not return the same value as the types.MemoryStats.Usage // The "docker stats" CLI call does not return the same value as the types.MemoryStats.Usage
// value which can be rather confusing to people trying to compare panel usage to // value which can be rather confusing to people trying to compare panel usage to
// their stats output. // their stats output.