Retry writing to cgroup files on EINTR error
Golang 1.14 introduces asynchronous preemption which results into applications getting frequent EINTR (syscall interrupted) errors when invoking slow syscalls, e.g. when writing to cgroup files. As writing to cgroups is idempotent, it is safe to retry writing to the file whenever the write syscall is interrupted. Signed-off-by: Mario Nitchev <marionitchev@gmail.com>
This commit is contained in:
parent
939cd0b734
commit
f34eb2c003
|
@ -4,9 +4,12 @@ package fscommon
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func WriteFile(dir, file, data string) error {
|
||||
|
@ -17,7 +20,7 @@ func WriteFile(dir, file, data string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(path, []byte(data), 0700); err != nil {
|
||||
if err := retryingWriteFile(path, []byte(data), 0700); err != nil {
|
||||
return errors.Wrapf(err, "failed to write %q to %q", data, path)
|
||||
}
|
||||
return nil
|
||||
|
@ -34,3 +37,24 @@ func ReadFile(dir, file string) (string, error) {
|
|||
data, err := ioutil.ReadFile(path)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
func retryingWriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||
for {
|
||||
err := ioutil.WriteFile(filename, data, perm)
|
||||
if isInterruptedWriteFile(err) {
|
||||
logrus.Infof("interrupted while writing %s to %s", string(data), filename)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func isInterruptedWriteFile(err error) bool {
|
||||
if patherr, ok := err.(*os.PathError); ok {
|
||||
errno, ok2 := patherr.Err.(syscall.Errno)
|
||||
if ok2 && errno == syscall.EINTR {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// +build linux
|
||||
|
||||
package fscommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
)
|
||||
|
||||
func TestWriteCgroupFileHandlesInterrupt(t *testing.T) {
|
||||
memoryCgroupMount, err := cgroups.FindCgroupMountpoint("", "memory")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cgroupName := fmt.Sprintf("test-eint-%d", time.Now().Nanosecond())
|
||||
cgroupPath := filepath.Join(memoryCgroupMount, cgroupName)
|
||||
if err := os.MkdirAll(cgroupPath, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cgroupPath)
|
||||
|
||||
for i := 0; i < 100000; i++ {
|
||||
limit := 1024*1024 + i
|
||||
if err := WriteFile(cgroupPath, "memory.limit_in_bytes", strconv.Itoa(limit)); err != nil {
|
||||
t.Fatalf("Failed to write %d on attempt %d: %+v", limit, i, err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue