Skip to content

Commit 0eea474

Browse files
committed
storage: avoid excessive walking of the auxiliary directory
Pebble incrementally accounts its disk usage and this accounting is used to cheaply service Capacity calls. The auxiliary directory is a subdirectory of the data directory, and various Cockroach subsystems write to it directly without any incremental accounting of their disk usage. As a result, Capacity calculations walk the auxiliary directory to calculate the directory's disk usage anew each time. Previously, this calculation occurred during every call to Capacity. This commit adapts Pebble.Capacity to cache the computed capacity and recalculate it at most every minute. Ideally, we would incrementally account disk usage within the auxiliary directory (#96344). This change is a stopgap, avoiding the bulk of the CPU usage incurred by Capacity recalculations during IMPORTs. Epic: none Release note: none
1 parent a3789d3 commit 0eea474

File tree

1 file changed

+61
-34
lines changed

1 file changed

+61
-34
lines changed

pkg/storage/pebble.go

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -803,10 +803,15 @@ type engineConfig struct {
803803

804804
// Pebble is a wrapper around a Pebble database instance.
805805
type Pebble struct {
806-
cfg engineConfig
807-
db *pebble.DB
808-
closed atomic.Bool
809-
auxDir string
806+
cfg engineConfig
807+
db *pebble.DB
808+
closed atomic.Bool
809+
auxDir string
810+
auxiliarySize struct {
811+
mu sync.Mutex
812+
computedAt time.Time
813+
size int64
814+
}
810815
ballastPath string
811816
properties roachpb.StoreProperties
812817

@@ -1919,37 +1924,11 @@ func (p *Pebble) Capacity() (roachpb.StoreCapacity, error) {
19191924
m := p.db.Metrics()
19201925
totalUsedBytes := int64(m.DiskSpaceUsage())
19211926

1922-
// We don't have incremental accounting of the disk space usage of files
1923-
// in the auxiliary directory. Walk the auxiliary directory and all its
1924-
// subdirectories, adding to the total used bytes.
1925-
if errOuter := filepath.Walk(p.auxDir, func(path string, info os.FileInfo, err error) error {
1926-
if err != nil {
1927-
// This can happen if CockroachDB removes files out from under us -
1928-
// just keep going to get the best estimate we can.
1929-
if oserror.IsNotExist(err) {
1930-
return nil
1931-
}
1932-
// Special-case: if the store-dir is configured using the root of some fs,
1933-
// e.g. "/mnt/db", we might have special fs-created files like lost+found
1934-
// that we can't read, so just ignore them rather than crashing.
1935-
if oserror.IsPermission(err) && filepath.Base(path) == "lost+found" {
1936-
return nil
1937-
}
1938-
return err
1939-
}
1940-
if path == p.ballastPath {
1941-
// Skip the ballast. Counting it as used is likely to confuse
1942-
// users, and it's more akin to space that is just unavailable
1943-
// like disk space often restricted to a root user.
1944-
return nil
1945-
}
1946-
if info.Mode().IsRegular() {
1947-
totalUsedBytes += info.Size()
1948-
}
1949-
return nil
1950-
}); errOuter != nil {
1951-
return roachpb.StoreCapacity{}, errOuter
1927+
auxiliarySize, err := p.auxiliaryDirSize()
1928+
if err != nil {
1929+
return roachpb.StoreCapacity{}, err
19521930
}
1931+
totalUsedBytes += auxiliarySize
19531932

19541933
// If no size limitation have been placed on the store size or if the
19551934
// limitation is greater than what's available, just return the actual
@@ -1982,6 +1961,54 @@ func (p *Pebble) Capacity() (roachpb.StoreCapacity, error) {
19821961
}, nil
19831962
}
19841963

1964+
// auxiliaryDirSize computes the size of the auxiliary directory. There are
1965+
// multiple Cockroach subsystems that write into the auxiliary directory, and
1966+
// they don't incrementally account for their disk space usage. This function
1967+
// walks the auxiliary directory and all its subdirectories, summing the file
1968+
// sizes. This walk can be expensive, so we cache the result and only recompute
1969+
// if it's over 1 minute stale.
1970+
//
1971+
// TODO(jackson): Eventually we should update the various subsystems writing
1972+
// into the auxiliary directory to incrementally account for their disk space
1973+
// usage. See #96344.
1974+
func (p *Pebble) auxiliaryDirSize() (int64, error) {
1975+
p.auxiliarySize.mu.Lock()
1976+
defer p.auxiliarySize.mu.Unlock()
1977+
if time.Since(p.auxiliarySize.computedAt) < time.Minute {
1978+
return p.auxiliarySize.size, nil
1979+
}
1980+
1981+
p.auxiliarySize.size = 0
1982+
err := filepath.Walk(p.auxDir, func(path string, info os.FileInfo, err error) error {
1983+
if err != nil {
1984+
// This can happen if CockroachDB removes files out from under us -
1985+
// just keep going to get the best estimate we can.
1986+
if oserror.IsNotExist(err) {
1987+
return nil
1988+
}
1989+
// Special-case: if the store-dir is configured using the root of some fs,
1990+
// e.g. "/mnt/db", we might have special fs-created files like lost+found
1991+
// that we can't read, so just ignore them rather than crashing.
1992+
if oserror.IsPermission(err) && filepath.Base(path) == "lost+found" {
1993+
return nil
1994+
}
1995+
return err
1996+
}
1997+
if path == p.ballastPath {
1998+
// Skip the ballast. Counting it as used is likely to confuse
1999+
// users, and it's more akin to space that is just unavailable
2000+
// like disk space often restricted to a root user.
2001+
return nil
2002+
}
2003+
if info.Mode().IsRegular() {
2004+
p.auxiliarySize.size += info.Size()
2005+
}
2006+
return nil
2007+
})
2008+
p.auxiliarySize.computedAt = time.Now()
2009+
return p.auxiliarySize.size, err
2010+
}
2011+
19852012
// Flush implements the Engine interface.
19862013
func (p *Pebble) Flush() error {
19872014
return p.db.Flush()

0 commit comments

Comments
 (0)