From f1df7d3f15ea835bcd5a0945eccec0528f814a4c Mon Sep 17 00:00:00 2001 From: Ningxuan Wang <43462394+FingerLeader@users.noreply.github.com> Date: Mon, 6 Jun 2022 10:48:29 +0800 Subject: [PATCH] redisChaos: add cache limit (#163) * add cache limit Signed-off-by: FingerLeader * add a comment Signed-off-by: FingerLeader * redis cache limit add a flag percent Signed-off-by: FingerLeader * edit some details Signed-off-by: FingerLeader * fix ci Signed-off-by: FingerLeader --- cmd/attack/redis.go | 19 +++++++++++++++ pkg/core/redis.go | 10 ++++++++ pkg/server/chaosd/redis.go | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/cmd/attack/redis.go b/cmd/attack/redis.go index d31c36b..c85fa5e 100644 --- a/cmd/attack/redis.go +++ b/cmd/attack/redis.go @@ -44,6 +44,7 @@ func NewRedisAttackCommand(uid *string) *cobra.Command { NewRedisSentinelRestartCommand(dep, options), NewRedisSentinelStopCommand(dep, options), NewRedisCachePenetrationCommand(dep, options), + NewRedisCacheLimitCommand(dep, options), ) return cmd @@ -105,6 +106,24 @@ func NewRedisCachePenetrationCommand(dep fx.Option, options *core.RedisCommand) 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) { if err := options.Validate(); err != nil { utils.ExitWithError(utils.ExitBadArgs, err) diff --git a/pkg/core/redis.go b/pkg/core/redis.go index aab4ba9..c736f7a 100644 --- a/pkg/core/redis.go +++ b/pkg/core/redis.go @@ -23,6 +23,7 @@ const ( RedisSentinelRestartAction = "restart" RedisSentinelStopAction = "stop" RedisCachePenetrationAction = "penetration" + RedisCacheLimitAction = "cacheLimit" ) var _ AttackConfig = &RedisCommand{} @@ -36,6 +37,10 @@ type RedisCommand struct { FlushConfig bool `json:"flushConfig,omitempty"` RedisPath string `json:"redisPath,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 { @@ -50,6 +55,11 @@ func (r *RedisCommand) Validate() error { if r.RequestNum == 0 { 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 } diff --git a/pkg/server/chaosd/redis.go b/pkg/server/chaosd/redis.go index fbfe723..ed6ade9 100644 --- a/pkg/server/chaosd/redis.go +++ b/pkg/server/chaosd/redis.go @@ -14,7 +14,10 @@ package chaosd import ( + "fmt" + "math" "os/exec" + "strconv" "github.com/go-redis/redis/v8" "github.com/pingcap/errors" @@ -62,6 +65,38 @@ func (redisAttack) Attack(options core.AttackConfig, env Environment) error { if err != redis.Nil { 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 } @@ -76,6 +111,20 @@ func (redisAttack) Recover(exp core.Experiment, env Environment) error { switch attack.Action { case core.RedisSentinelStopAction: 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 }