github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/functional/tester/stresser_runner.go (about) 1 // Copyright 2018 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tester 16 17 import ( 18 "fmt" 19 "io" 20 "os/exec" 21 "syscall" 22 23 "github.com/lfch/etcd-io/tests/v3/functional/rpcpb" 24 25 "go.uber.org/zap" 26 "golang.org/x/time/rate" 27 ) 28 29 type runnerStresser struct { 30 stype rpcpb.StresserType 31 etcdClientEndpoint string 32 lg *zap.Logger 33 34 cmd *exec.Cmd 35 cmdStr string 36 args []string 37 rl *rate.Limiter 38 reqRate int 39 40 errc chan error 41 donec chan struct{} 42 } 43 44 func newRunnerStresser( 45 stype rpcpb.StresserType, 46 ep string, 47 lg *zap.Logger, 48 cmdStr string, 49 args []string, 50 rl *rate.Limiter, 51 reqRate int, 52 ) *runnerStresser { 53 rl.SetLimit(rl.Limit() - rate.Limit(reqRate)) 54 return &runnerStresser{ 55 stype: stype, 56 etcdClientEndpoint: ep, 57 lg: lg, 58 cmdStr: cmdStr, 59 args: args, 60 rl: rl, 61 reqRate: reqRate, 62 errc: make(chan error, 1), 63 donec: make(chan struct{}), 64 } 65 } 66 67 func (rs *runnerStresser) setupOnce() (err error) { 68 if rs.cmd != nil { 69 return nil 70 } 71 72 rs.cmd = exec.Command(rs.cmdStr, rs.args...) 73 stderr, err := rs.cmd.StderrPipe() 74 if err != nil { 75 return err 76 } 77 78 go func() { 79 defer close(rs.donec) 80 out, err := io.ReadAll(stderr) 81 if err != nil { 82 rs.errc <- err 83 } else { 84 rs.errc <- fmt.Errorf("(%v %v) stderr %v", rs.cmdStr, rs.args, string(out)) 85 } 86 }() 87 88 return rs.cmd.Start() 89 } 90 91 func (rs *runnerStresser) Stress() (err error) { 92 rs.lg.Info( 93 "stress START", 94 zap.String("stress-type", rs.stype.String()), 95 ) 96 if err = rs.setupOnce(); err != nil { 97 return err 98 } 99 return syscall.Kill(rs.cmd.Process.Pid, syscall.SIGCONT) 100 } 101 102 func (rs *runnerStresser) Pause() map[string]int { 103 rs.lg.Info( 104 "stress STOP", 105 zap.String("stress-type", rs.stype.String()), 106 ) 107 syscall.Kill(rs.cmd.Process.Pid, syscall.SIGSTOP) 108 return nil 109 } 110 111 func (rs *runnerStresser) Close() map[string]int { 112 syscall.Kill(rs.cmd.Process.Pid, syscall.SIGINT) 113 rs.cmd.Wait() 114 <-rs.donec 115 rs.rl.SetLimit(rs.rl.Limit() + rate.Limit(rs.reqRate)) 116 return nil 117 } 118 119 func (rs *runnerStresser) ModifiedKeys() int64 { 120 return 1 121 }