Repository: zeromicro/go-zero
Pull request #2986 (2023-03-08 09:16:47)

reids cluster bug


Patch:
diff --git a/core/logx/config.go b/core/logx/config.go
index 1bd2317d..2b00f276 100644
--- a/core/logx/config.go
+++ b/core/logx/config.go
@@ -23,7 +23,7 @@ type LogConf struct {
 	MaxContentLength uint32 `json:",optional"`
 	// Compress represents whether to compress the log file, default is `false`.
 	Compress bool `json:",optional"`
-	// Stdout represents whether to log statistics, default is `true`.
+	// Stat represents whether to log statistics, default is `true`.
 	Stat bool `json:",default=true"`
 	// KeepDays represents how many days the log files will be kept. Default to keep all files.
 	// Only take effect when Mode is `file` or `volume`, both work when Rotation is `daily` or `size`.
@@ -38,7 +38,7 @@ type LogConf struct {
 	// MaxSize represents how much space the writing log file takes up. 0 means no limit. The unit is `MB`.
 	// Only take effect when RotationRuleType is `size`
 	MaxSize int `json:",default=0"`
-	// RotationRuleType represents the type of log rotation rule. Default is `daily`.
+	// Rotation represents the type of log rotation rule. Default is `daily`.
 	// daily: daily rotation.
 	// size: size limited rotation.
 	Rotation string `json:",default=daily,options=[daily,size]"`
diff --git a/core/mapping/valuer.go b/core/mapping/valuer.go
index 37f0b3bd..206b1b13 100644
--- a/core/mapping/valuer.go
+++ b/core/mapping/valuer.go
@@ -34,7 +34,7 @@ type (
 	recursiveValuer node
 )
 
-// Value gets the value assciated with the given key from mv.
+// Value gets the value associated with the given key from mv.
 func (mv mapValuer) Value(key string) (any, bool) {
 	v, ok := mv[key]
 	return v, ok
diff --git a/core/stores/redis/redis.go b/core/stores/redis/redis.go
index 7ee06ea9..31c7e514 100644
--- a/core/stores/redis/redis.go
+++ b/core/stores/redis/redis.go
@@ -832,12 +832,12 @@ func (s *Redis) HincrbyCtx(ctx context.Context, key, field string, increment int
 	return
 }
 
-// HincrbyFloat is the implementation of redis hincrby command.
+// HincrbyFloat is the implementation of redis hincrbyfloat command.
 func (s *Redis) HincrbyFloat(key, field string, increment float64) (float64, error) {
 	return s.HincrbyFloatCtx(context.Background(), key, field, increment)
 }
 
-// HincrbyFloatCtx is the implementation of redis hincrby command.
+// HincrbyFloatCtx is the implementation of redis hincrbyfloat command.
 func (s *Redis) HincrbyFloatCtx(ctx context.Context, key, field string, increment float64) (val float64, err error) {
 	err = s.brk.DoWithAcceptable(func() error {
 		conn, err := getRedis(s)
@@ -1065,12 +1065,12 @@ func (s *Redis) IncrbyCtx(ctx context.Context, key string, increment int64) (val
 	return
 }
 
-// IncrbyFloat is the implementation of redis incrby command.
+// IncrbyFloat is the implementation of redis hincrbyfloat command.
 func (s *Redis) IncrbyFloat(key string, increment float64) (float64, error) {
 	return s.IncrbyFloatCtx(context.Background(), key, increment)
 }
 
-// IncrbyFloatCtx is the implementation of redis incrby command.
+// IncrbyFloatCtx is the implementation of redis hincrbyfloat command.
 func (s *Redis) IncrbyFloatCtx(ctx context.Context, key string, increment float64) (val float64, err error) {
 	err = s.brk.DoWithAcceptable(func() error {
 		conn, err := getRedis(s)
@@ -1170,6 +1170,26 @@ func (s *Redis) LpopCtx(ctx context.Context, key string) (val string, err error)
 	return
 }
 
+// LpopCount is the implementation of redis lpopCount command.
+func (s *Redis) LpopCount(key string, count int) ([]string, error) {
+	return s.LpopCountCtx(context.Background(), key, count)
+}
+
+// LpopCountCtx is the implementation of redis lpopCount command.
+func (s *Redis) LpopCountCtx(ctx context.Context, key string, count int) (val []string, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.LPopCount(ctx, key, count).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
 // Lpush is the implementation of redis lpush command.
 func (s *Redis) Lpush(key string, values ...any) (int, error) {
 	return s.LpushCtx(context.Background(), key, values...)
@@ -1432,6 +1452,26 @@ func (s *Redis) RpopCtx(ctx context.Context, key string) (val string, err error)
 	return
 }
 
+// RpopCount is the implementation of redis rpopCount command.
+func (s *Redis) RpopCount(key string, count int) ([]string, error) {
+	return s.RpopCountCtx(context.Background(), key, count)
+}
+
+// RpopCountCtx is the implementation of redis rpopCount command.
+func (s *Redis) RpopCountCtx(ctx context.Context, key string, count int) (val []string, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.RPopCount(ctx, key, count).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
 // Rpush is the implementation of redis rpush command.
 func (s *Redis) Rpush(key string, values ...any) (int, error) {
 	return s.RpushCtx(context.Background(), key, values...)
diff --git a/core/stores/redis/redisblockingnode.go b/core/stores/redis/redisblockingnode.go
index 696b8860..bdb47871 100644
--- a/core/stores/redis/redisblockingnode.go
+++ b/core/stores/redis/redisblockingnode.go
@@ -2,6 +2,7 @@ package redis
 
 import (
 	"fmt"
+	"strings"
 
 	red "github.com/go-redis/redis/v8"
 	"github.com/zeromicro/go-zero/core/logx"
@@ -31,7 +32,7 @@ func CreateBlockingNode(r *Redis) (ClosableNode, error) {
 		return &clientBridge{client}, nil
 	case ClusterType:
 		client := red.NewClusterClient(&red.ClusterOptions{
-			Addrs:        []string{r.Addr},
+			Addrs:        strings.Split(r.Addr, ","),
 			Password:     r.Pass,
 			MaxRetries:   maxRetries,
 			PoolSize:     1,
diff --git a/core/stores/redis/redisclustermanager.go b/core/stores/redis/redisclustermanager.go
index de8b1203..1f1e1b19 100644
--- a/core/stores/redis/redisclustermanager.go
+++ b/core/stores/redis/redisclustermanager.go
@@ -3,6 +3,7 @@ package redis
 import (
 	"crypto/tls"
 	"io"
+	"strings"
 
 	red "github.com/go-redis/redis/v8"
 	"github.com/zeromicro/go-zero/core/syncx"
@@ -19,7 +20,7 @@ func getCluster(r *Redis) (*red.ClusterClient, error) {
 			}
 		}
 		store := red.NewClusterClient(&red.ClusterOptions{
-			Addrs:        []string{r.Addr},
+			Addrs:        strings.Split(r.Addr, ","),
 			Password:     r.Pass,
 			MaxRetries:   maxRetries,
 			MinIdleConns: idleConns,
diff --git a/go.mod b/go.mod
index 0f0a095b..0a875afb 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.18
 
 require (
 	github.com/DATA-DOG/go-sqlmock v1.5.0
-	github.com/alicebob/miniredis/v2 v2.30.0
+	github.com/alicebob/miniredis/v2 v2.30.1
 	github.com/fatih/color v1.14.1
 	github.com/felixge/fgprof v0.9.3
 	github.com/fullstorydev/grpcurl v1.8.7
@@ -12,10 +12,10 @@ require (
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/golang/mock v1.6.0
-	github.com/golang/protobuf v1.5.2
+	github.com/golang/protobuf v1.5.3
 	github.com/google/uuid v1.3.0
 	github.com/jackc/pgx/v5 v5.3.1
-	github.com/jhump/protoreflect v1.15.0
+	github.com/jhump/protoreflect v1.15.1
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/pelletier/go-toml/v2 v2.0.7
 	github.com/prometheus/client_golang v1.14.0
@@ -33,11 +33,11 @@ require (
 	go.opentelemetry.io/otel/trace v1.14.0
 	go.uber.org/automaxprocs v1.5.1
 	go.uber.org/goleak v1.2.1
-	golang.org/x/sys v0.5.0
+	golang.org/x/sys v0.6.0
 	golang.org/x/time v0.3.0
 	google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197
 	google.golang.org/grpc v1.53.0
-	google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8
+	google.golang.org/protobuf v1.29.0
 	gopkg.in/cheggaaa/pb.v1 v1.0.28
 	gopkg.in/h2non/gock.v1 v1.1.2
 	gopkg.in/yaml.v2 v2.4.0
@@ -50,7 +50,7 @@ require (
 require (
 	github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f // indirect
+	github.com/bufbuild/protocompile v0.4.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.2.0 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/coreos/go-semver v0.3.1 // indirect
@@ -96,7 +96,7 @@ require (
 	github.com/xdg-go/scram v1.1.1 // indirect
 	github.com/xdg-go/stringprep v1.0.3 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
-	github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 // indirect
+	github.com/yuin/gopher-lua v1.1.0 // indirect
 	go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
diff --git a/go.sum b/go.sum
index 7e3835ea..095e2ba8 100644
--- a/go.sum
+++ b/go.sum
@@ -43,16 +43,16 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
 github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
-github.com/alicebob/miniredis/v2 v2.30.0 h1:uA3uhDbCxfO9+DI/DuGeAMr9qI+noVWwGPNTFuKID5M=
-github.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=
+github.com/alicebob/miniredis/v2 v2.30.1 h1:HM1rlQjq1bm9yQcsawJqSZBJ9AYgxvjkMsNtddh90+g=
+github.com/alicebob/miniredis/v2 v2.30.1/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f h1:IXSA5gow10s7zIOJfPOpXDtNBWCTA0715BDAhoJBXEs=
-github.com/bufbuild/protocompile v0.2.1-0.20230123224550-da57cd758c2f/go.mod h1:tleDrpPTlLUVmgnEoN6qBliKWqJaZFJXqZdFjTd+ocU=
+github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
+github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
 github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
 github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -169,8 +169,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -232,8 +233,8 @@ github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+
 github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
 github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
 github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
-github.com/jhump/protoreflect v1.15.0 h1:U5T5/2LF0AZQFP9T4W5GfBjBaTruomrKobiR4E+oA/Q=
-github.com/jhump/protoreflect v1.15.0/go.mod h1:qww51KYjD2hoCl/ohxw5cK2LSssFczrbO1t8Ld2TENs=
+github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
+github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -379,8 +380,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
-github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
+github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
+github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
 go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
 go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
 go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=
@@ -569,8 +570,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
@@ -729,8 +730,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk=
-google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
+google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/tools/goctl/api/cmd.go b/tools/goctl/api/cmd.go
index 0545a5ce..3c550f30 100644
--- a/tools/goctl/api/cmd.go
+++ b/tools/goctl/api/cmd.go
@@ -106,6 +106,7 @@ func init() {
 	dartCmd.Flags().StringVar(&dartgen.VarStringAPI, "api", "", "The api file")
 	dartCmd.Flags().BoolVar(&dartgen.VarStringLegacy, "legacy", false, "Legacy generator for flutter v1")
 	dartCmd.Flags().StringVar(&dartgen.VarStringHostname, "hostname", "", "hostname of the server")
+	dartCmd.Flags().StringVar(&dartgen.VarStringScheme, "scheme", "", "scheme of the server")
 
 	docCmd.Flags().StringVar(&docgen.VarStringDir, "dir", "", "The target dir")
 	docCmd.Flags().StringVar(&docgen.VarStringOutput, "o", "", "The output markdown directory")
diff --git a/tools/goctl/api/dartgen/gen.go b/tools/goctl/api/dartgen/gen.go
index b73e3901..edf90fd2 100644
--- a/tools/goctl/api/dartgen/gen.go
+++ b/tools/goctl/api/dartgen/gen.go
@@ -19,6 +19,8 @@ var (
 	VarStringLegacy bool
 	// VarStringHostname defines the hostname.
 	VarStringHostname string
+	// VarStringSchema defines the scheme.
+	VarStringScheme string
 )
 
 // DartCommand create dart network request code
@@ -27,6 +29,7 @@ func DartCommand(_ *cobra.Command, _ []string) error {
 	dir := VarStringDir
 	isLegacy := VarStringLegacy
 	hostname := VarStringHostname
+	scheme := VarStringScheme
 	if len(apiFile) == 0 {
 		return errors.New("missing -api")
 	}
@@ -37,6 +40,10 @@ func DartCommand(_ *cobra.Command, _ []string) error {
 		fmt.Println("you could use '-hostname' flag to specify your server hostname")
 		hostname = "go-zero.dev"
 	}
+	if len(scheme) == 0 {
+		fmt.Println("you could use '-scheme' flag to specify your server scheme")
+		scheme = "http"
+	}
 
 	api, err := parser.Parse(apiFile)
 	if err != nil {
@@ -54,7 +61,7 @@ func DartCommand(_ *cobra.Command, _ []string) error {
 	api.Info.Title = strings.Replace(apiFile, ".api", "", -1)
 	logx.Must(genData(dir+"data/", api, isLegacy))
 	logx.Must(genApi(dir+"api/", api, isLegacy))
-	logx.Must(genVars(dir+"vars/", isLegacy, hostname))
+	logx.Must(genVars(dir+"vars/", isLegacy, scheme, hostname))
 	if err := formatDir(dir); err != nil {
 		logx.Errorf("failed to format, %v", err)
 	}
diff --git a/tools/goctl/api/dartgen/genvars.go b/tools/goctl/api/dartgen/genvars.go
index 72ee6bdc..ac1b0fac 100644
--- a/tools/goctl/api/dartgen/genvars.go
+++ b/tools/goctl/api/dartgen/genvars.go
@@ -76,14 +76,14 @@ Future<Tokens?> getTokens() async {
 }`
 )
 
-func genVars(dir string, isLegacy bool, hostname string) error {
+func genVars(dir string, isLegacy bool, scheme string, hostname string) error {
 	err := os.MkdirAll(dir, 0o755)
 	if err != nil {
 		return err
 	}
 
 	if !fileExists(dir + "vars.dart") {
-		err = ioutil.WriteFile(dir+"vars.dart", []byte(fmt.Sprintf(`const serverHost='%s';`, hostname)), 0o644)
+		err = ioutil.WriteFile(dir+"vars.dart", []byte(fmt.Sprintf(`const serverHost='%s://%s';`, scheme, hostname)), 0o644)
 		if err != nil {
 			return err
 		}
diff --git a/tools/goctl/api/dartgen/vars.go b/tools/goctl/api/dartgen/vars.go
index b34c3a6e..1a304f5b 100644
--- a/tools/goctl/api/dartgen/vars.go
+++ b/tools/goctl/api/dartgen/vars.go
@@ -56,12 +56,21 @@ Future _apiRequest(String method, String path, dynamic data,
     var client = HttpClient();
     HttpClientRequest r;
     if (method == 'POST') {
-      r = await client.postUrl(Uri.parse('https://' + serverHost + path));
+      r = await client.postUrl(Uri.parse(serverHost + path));
     } else {
-      r = await client.getUrl(Uri.parse('https://' + serverHost + path));
+      r = await client.getUrl(Uri.parse(serverHost + path));
+    }
+
+    var strData = '';
+    if (data != null) {
+      strData = jsonEncode(data);
+    }
+
+    if (method == 'POST') {
+      r.headers.set('Content-Type', 'application/json; charset=utf-8');
+      r.headers.set('Content-Length', utf8.encode(strData).length);
     }
 
-    r.headers.set('Content-Type', 'application/json; charset=utf-8');
     if (tokens != null) {
       r.headers.set('Authorization', tokens.accessToken);
     }
@@ -70,11 +79,9 @@ Future _apiRequest(String method, String path, dynamic data,
         r.headers.set(k, v);
       });
     }
-    var strData = '';
-    if (data != null) {
-      strData = jsonEncode(data);
-    }
+
     r.write(strData);
+
     var rp = await r.close();
     var body = await rp.transform(utf8.decoder).join();
     print('${rp.statusCode} - $path');
@@ -147,12 +154,19 @@ Future _apiRequest(String method, String path, dynamic data,
 			var client = HttpClient();
 			HttpClientRequest r;
 			if (method == 'POST') {
-				r = await client.postUrl(Uri.parse('https://' + serverHost + path));
+				r = await client.postUrl(Uri.parse(serverHost + path));
 			} else {
-				r = await client.getUrl(Uri.parse('https://' + serverHost + path));
+				r = await client.getUrl(Uri.parse(serverHost + path));
 			}
 
-			r.headers.set('Content-Type', 'application/json; charset=utf-8');
+      var strData = '';
+			if (data != null) {
+				strData = jsonEncode(data);
+			}
+			if (method == 'POST') {
+        r.headers.set('Content-Type', 'application/json; charset=utf-8');
+        r.headers.set('Content-Length', utf8.encode(strData).length);
+      }
 			if (tokens != null) {
 				r.headers.set('Authorization', tokens.accessToken);
 			}
@@ -161,10 +175,7 @@ Future _apiRequest(String method, String path, dynamic data,
 					r.headers.set(k, v);
 				});
 			}
-			var strData = '';
-			if (data != null) {
-				strData = jsonEncode(data);
-			}
+
 			r.write(strData);
 			var rp = await r.close();
 			var body = await rp.transform(utf8.decoder).join();
diff --git a/tools/goctl/model/sql/template/tpl/model-new.tpl b/tools/goctl/model/sql/template/tpl/model-new.tpl
index c73ab593..76598ecd 100644
--- a/tools/goctl/model/sql/template/tpl/model-new.tpl
+++ b/tools/goctl/model/sql/template/tpl/model-new.tpl
@@ -1,6 +1,6 @@
-func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *default{{.upperStartCamelObject}}Model {
+func new{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) *default{{.upperStartCamelObject}}Model {
 	return &default{{.upperStartCamelObject}}Model{
-		{{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}},
+		{{if .withCache}}CachedConn: sqlc.NewConn(conn, c, opts...){{else}}conn:conn{{end}},
 		table:      {{.table}},
 	}
 }
diff --git a/tools/goctl/model/sql/template/tpl/model.tpl b/tools/goctl/model/sql/template/tpl/model.tpl
index 2ca914a4..3b1b1013 100644
--- a/tools/goctl/model/sql/template/tpl/model.tpl
+++ b/tools/goctl/model/sql/template/tpl/model.tpl
@@ -23,8 +23,8 @@ type (
 )
 
 // New{{.upperStartCamelObject}}Model returns a model for the database table.
-func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model {
+func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf, opts ...cache.Option{{end}}) {{.upperStartCamelObject}}Model {
 	return &custom{{.upperStartCamelObject}}Model{
-		default{{.upperStartCamelObject}}Model: new{{.upperStartCamelObject}}Model(conn{{if .withCache}}, c{{end}}),
+		default{{.upperStartCamelObject}}Model: new{{.upperStartCamelObject}}Model(conn{{if .withCache}}, c, opts...{{end}}),
 	}
 }


Test patch:
diff --git a/core/stores/redis/redis_test.go b/core/stores/redis/redis_test.go
index 88ddd48b..ffd4d00d 100644
--- a/core/stores/redis/redis_test.go
+++ b/core/stores/redis/redis_test.go
@@ -507,6 +507,14 @@ func TestRedis_List(t *testing.T) {
 			vals, err = client.Lrange("key", 0, 10)
 			assert.Nil(t, err)
 			assert.EqualValues(t, []string{"value2", "value3"}, vals)
+			vals, err = client.LpopCount("key", 2)
+			assert.Nil(t, err)
+			assert.EqualValues(t, []string{"value2", "value3"}, vals)
+			_, err = client.Lpush("key", "value1", "value2")
+			assert.Nil(t, err)
+			vals, err = client.RpopCount("key", 4)
+			assert.Nil(t, err)
+			assert.EqualValues(t, []string{"value1", "value2"}, vals)
 		})
 	})
 
@@ -523,6 +531,34 @@ func TestRedis_List(t *testing.T) {
 
 			_, err = client.Rpush("key", "value3", "value4")
 			assert.Error(t, err)
+
+			_, err = client.LpopCount("key", 2)
+			assert.Error(t, err)
+
+			_, err = client.RpopCount("key", 2)
+			assert.Error(t, err)
+		})
+	})
+	t.Run("list redis type error", func(t *testing.T) {
+		runOnRedisWithError(t, func(client *Redis) {
+			client.Type = "nil"
+			_, err := client.Llen("key")
+			assert.Error(t, err)
+
+			_, err = client.Lpush("key", "value1", "value2")
+			assert.Error(t, err)
+
+			_, err = client.Lrem("key", 2, "value1")
+			assert.Error(t, err)
+
+			_, err = client.Rpush("key", "value3", "value4")
+			assert.Error(t, err)
+
+			_, err = client.LpopCount("key", 2)
+			assert.Error(t, err)
+
+			_, err = client.RpopCount("key", 2)
+			assert.Error(t, err)
 		})
 	})
 }