redisChaos: add cache limit (#163)

* add cache limit

Signed-off-by: FingerLeader <wanxfinger@gmail.com>

* add a comment

Signed-off-by: FingerLeader <wanxfinger@gmail.com>

* redis cache limit add a flag percent

Signed-off-by: FingerLeader <wanxfinger@gmail.com>

* edit some details

Signed-off-by: FingerLeader <wanxfinger@gmail.com>

* fix ci

Signed-off-by: FingerLeader <wanxfinger@gmail.com>
This commit is contained in:
Ningxuan Wang 2022-06-06 10:48:29 +08:00 committed by GitHub
parent 3cb3a334f6
commit f1df7d3f15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 0 deletions

View File

@ -44,6 +44,7 @@ func NewRedisAttackCommand(uid *string) *cobra.Command {
NewRedisSentinelRestartCommand(dep, options), NewRedisSentinelRestartCommand(dep, options),
NewRedisSentinelStopCommand(dep, options), NewRedisSentinelStopCommand(dep, options),
NewRedisCachePenetrationCommand(dep, options), NewRedisCachePenetrationCommand(dep, options),
NewRedisCacheLimitCommand(dep, options),
) )
return cmd return cmd
@ -105,6 +106,24 @@ func NewRedisCachePenetrationCommand(dep fx.Option, options *core.RedisCommand)
return cmd return cmd
} }
func NewRedisCacheLimitCommand(dep fx.Option, options *core.RedisCommand) *cobra.Command {
cmd := &cobra.Command{
Use: "cache-limit",
Short: "set maxmemory of Redis",
Run: func(*cobra.Command, []string) {
options.Action = core.RedisCacheLimitAction
utils.FxNewAppWithoutLog(dep, fx.Invoke(redisAttackF)).Run()
},
}
cmd.Flags().StringVarP(&options.Addr, "addr", "a", "", "")
cmd.Flags().StringVarP(&options.Password, "password", "p", "", "The password of server")
cmd.Flags().StringVarP(&options.CacheSize, "size", "s", "0", "The size of cache")
cmd.Flags().StringVarP(&options.Percent, "percent", "", "", "The percentage of maxmemory")
return cmd
}
func redisAttackF(chaos *chaosd.Server, options *core.RedisCommand) { func redisAttackF(chaos *chaosd.Server, options *core.RedisCommand) {
if err := options.Validate(); err != nil { if err := options.Validate(); err != nil {
utils.ExitWithError(utils.ExitBadArgs, err) utils.ExitWithError(utils.ExitBadArgs, err)

View File

@ -23,6 +23,7 @@ const (
RedisSentinelRestartAction = "restart" RedisSentinelRestartAction = "restart"
RedisSentinelStopAction = "stop" RedisSentinelStopAction = "stop"
RedisCachePenetrationAction = "penetration" RedisCachePenetrationAction = "penetration"
RedisCacheLimitAction = "cacheLimit"
) )
var _ AttackConfig = &RedisCommand{} var _ AttackConfig = &RedisCommand{}
@ -36,6 +37,10 @@ type RedisCommand struct {
FlushConfig bool `json:"flushConfig,omitempty"` FlushConfig bool `json:"flushConfig,omitempty"`
RedisPath string `json:"redisPath,omitempty"` RedisPath string `json:"redisPath,omitempty"`
RequestNum int `json:"requestNum,omitempty"` RequestNum int `json:"requestNum,omitempty"`
CacheSize string `json:"cacheSize,omitempty"`
Percent string `json:"percent,omitempty"`
OriginCacheSize string `json:"originCacheSize,omitempty"`
} }
func (r *RedisCommand) Validate() error { func (r *RedisCommand) Validate() error {
@ -50,6 +55,11 @@ func (r *RedisCommand) Validate() error {
if r.RequestNum == 0 { if r.RequestNum == 0 {
return errors.New("request-num is required") return errors.New("request-num is required")
} }
case RedisCacheLimitAction:
if r.CacheSize != "0" && r.Percent != "" {
return errors.New("only one of cachesize and percent can be set")
}
} }
return nil return nil
} }

View File

@ -14,7 +14,10 @@
package chaosd package chaosd
import ( import (
"fmt"
"math"
"os/exec" "os/exec"
"strconv"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"github.com/pingcap/errors" "github.com/pingcap/errors"
@ -62,6 +65,38 @@ func (redisAttack) Attack(options core.AttackConfig, env Environment) error {
if err != redis.Nil { if err != redis.Nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
case core.RedisCacheLimitAction:
// `maxmemory` is an interface listwith content similar to `[maxmemory 1024]`
maxmemory, err := cli.ConfigGet(cli.Context(), "maxmemory").Result()
if err != nil {
return errors.WithStack(err)
}
// Get the value of maxmemory
attack.OriginCacheSize = fmt.Sprint(maxmemory[1])
var cacheSize string
if attack.Percent != "" {
percentage, err := strconv.ParseFloat(attack.Percent[0:len(attack.Percent)-1], 64)
if err != nil {
return errors.WithStack(err)
}
originCacheSize, err := strconv.ParseFloat(attack.OriginCacheSize, 64)
if err != nil {
return errors.WithStack(err)
}
cacheSize = fmt.Sprint(int(math.Floor(originCacheSize / 100.0 * percentage)))
} else {
cacheSize = attack.CacheSize
}
result, err := cli.ConfigSet(cli.Context(), "maxmemory", cacheSize).Result()
if err != nil {
return errors.WithStack(err)
}
if result != STATUSOK {
return errors.WithStack(errors.Errorf("redis command status is %s", result))
}
} }
return nil return nil
} }
@ -76,6 +111,20 @@ func (redisAttack) Recover(exp core.Experiment, env Environment) error {
switch attack.Action { switch attack.Action {
case core.RedisSentinelStopAction: case core.RedisSentinelStopAction:
return env.Chaos.recoverSentinelStop(attack) return env.Chaos.recoverSentinelStop(attack)
case core.RedisCacheLimitAction:
cli := redis.NewClient(&redis.Options{
Addr: attack.Addr,
Password: attack.Password,
})
result, err := cli.ConfigSet(cli.Context(), "maxmemory", attack.OriginCacheSize).Result()
if err != nil {
return errors.WithStack(err)
}
if result != STATUSOK {
return errors.WithStack(errors.Errorf("redis command status is %s", result))
}
} }
return nil return nil
} }