@@ -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
588589func 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