Skip to content

Commit 9b3cae7

Browse files
authored
Fix robustness engine i/o limit test flake (kopia#864)
Instead of a flaky timing check, calculate the amount of data that was actually written to the fio file tree.
1 parent 7e57984 commit 9b3cae7

File tree

1 file changed

+53
-15
lines changed

1 file changed

+53
-15
lines changed

tests/robustness/engine/engine_test.go

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"errors"
1111
"fmt"
1212
"io"
13+
"io/fs"
1314
"io/ioutil"
1415
"log"
1516
"math"
@@ -586,13 +587,9 @@ func TestActionsS3(t *testing.T) {
586587
}
587588

588589
func TestIOLimitPerWriteAction(t *testing.T) {
589-
// Instruct a write action to write an enormous amount of data
590-
// that should take longer than this timeout without "io_limit",
591-
// but finish in less time with "io_limit". Command instructs fio
592-
// to generate 100 files x 10 MB each = 1 GB of i/o. The limit is
593-
// set to 1 MB.
594-
const timeout = 10 * time.Second
595-
590+
// Instruct a write action to write a large amount of data, but add
591+
// an I/O limit parameter. Expect that the FIO action should limit
592+
// the amount of data it writes.
596593
os.Setenv(snapmeta.EngineModeEnvKey, snapmeta.EngineModeBasic)
597594
os.Setenv(snapmeta.S3BucketNameEnvKey, "")
598595

@@ -614,6 +611,8 @@ func TestIOLimitPerWriteAction(t *testing.T) {
614611
err = eng.Init(ctx)
615612
testenv.AssertNoError(t, err)
616613

614+
ioLimitBytes := 1 * 1024 * 1024
615+
617616
actionOpts := ActionOpts{
618617
ActionControlActionKey: map[string]string{
619618
string(SnapshotRootDirActionKey): strconv.Itoa(0),
@@ -628,20 +627,59 @@ func TestIOLimitPerWriteAction(t *testing.T) {
628627
fiofilewriter.MinFileSizeField: strconv.Itoa(10 * 1024 * 1024),
629628
fiofilewriter.MaxNumFilesPerWriteField: "100",
630629
fiofilewriter.MinNumFilesPerWriteField: "100",
631-
fiofilewriter.IOLimitPerWriteAction: strconv.Itoa(1 * 1024 * 1024),
630+
fiofilewriter.IOLimitPerWriteAction: strconv.Itoa(ioLimitBytes),
632631
},
633632
}
634633

635-
st := clock.Now()
634+
err = eng.RandomAction(actionOpts)
635+
testenv.AssertNoError(t, err)
636636

637-
numActions := 1
638-
for loop := 0; loop < numActions; loop++ {
639-
err := eng.RandomAction(actionOpts)
640-
testenv.AssertNoError(t, err)
637+
size := 0
638+
639+
// The FIO write operation is expected to create multiple files.
640+
// The behavior is that I/O will begin on a file, writing randomly
641+
// to that file until the I/O limit is hit. Thus we expect to see
642+
// one file with non-zero size (should be approx. between min-max file size
643+
// parameters, i.e. 10 MiB) and 1 MiB or less of non-zero data
644+
// written to it, due to the I/O limit.
645+
walkFunc := func(path string, info fs.FileInfo, err error) error {
646+
if !info.IsDir() && info.Size() > 0 {
647+
fileContentB, err := ioutil.ReadFile(path)
648+
testenv.AssertNoError(t, err)
649+
650+
nonZeroByteCount := 0
651+
652+
for _, byteVal := range fileContentB {
653+
if byteVal > 0 {
654+
nonZeroByteCount++
655+
}
656+
}
657+
658+
size += nonZeroByteCount
659+
}
660+
661+
return nil
641662
}
642663

643-
if clock.Since(st) > timeout {
644-
t.Errorf("IO limit parameter did not cut down on the fio runtime")
664+
fioPath := eng.FileWriter.DataDirectory()
665+
666+
// Walk the FIO data directory tree, counting the non-zero data written.
667+
err = filepath.Walk(fioPath, walkFunc)
668+
testenv.AssertNoError(t, err)
669+
670+
if got, want := size, ioLimitBytes; got > want {
671+
t.Fatalf("IO write limit exceeded for write action. Wrote %v B with io limit %v", got, want)
672+
}
673+
674+
// We might expect that a '0' gets written as part of the FIO data. This
675+
// means the count of non-zero bytes above might be a bit less than the exact
676+
// i/o limit parameter. We shouldn't expect a large percent of '0' though, so
677+
// this check will ensure fio didn't write significantly less than the limit.
678+
// A fraction of at least 95% non-zero should be very conservative.
679+
const thresholdNonZeroFraction = 0.95
680+
681+
if got, want := float64(size), float64(ioLimitBytes)*thresholdNonZeroFraction; got <= want {
682+
t.Fatalf("IO write limit exceeded for write action. Wrote %v B with io limit %v", got, want)
645683
}
646684
}
647685

0 commit comments

Comments
 (0)