diff --git a/clientv3/integration/txn_test.go b/clientv3/integration/txn_test.go index 1b6b808ecac..042794aee44 100644 --- a/clientv3/integration/txn_test.go +++ b/clientv3/integration/txn_test.go @@ -41,7 +41,7 @@ func TestTxnError(t *testing.T) { t.Fatalf("expected %v, got %v", rpctypes.ErrDuplicateKey, err) } - ops := make([]clientv3.Op, v3rpc.MaxOpsPerTxn+10) + ops := make([]clientv3.Op, v3rpc.MaxTxnOps+10) for i := range ops { ops[i] = clientv3.OpPut(fmt.Sprintf("foo%d", i), "") } diff --git a/embed/config.go b/embed/config.go index 24214530163..6e1725186bf 100644 --- a/embed/config.go +++ b/embed/config.go @@ -38,6 +38,7 @@ const ( DefaultName = "default" DefaultMaxSnapshots = 5 DefaultMaxWALs = 5 + DefaultMaxTxnOps = uint(128) DefaultListenPeerURLs = "http://localhost:2380" DefaultListenClientURLs = "http://localhost:2379" @@ -83,6 +84,7 @@ type Config struct { TickMs uint `json:"heartbeat-interval"` ElectionMs uint `json:"election-timeout"` QuotaBackendBytes int64 `json:"quota-backend-bytes"` + MaxTxnOps uint `json:"max-txn-ops"` // clustering @@ -157,6 +159,7 @@ func NewConfig() *Config { MaxWalFiles: DefaultMaxWALs, Name: DefaultName, SnapCount: etcdserver.DefaultSnapCount, + MaxTxnOps: DefaultMaxTxnOps, TickMs: 100, ElectionMs: 1000, LPUrls: []url.URL{*lpurl}, diff --git a/embed/etcd.go b/embed/etcd.go index a33a8554625..102b3bc30e4 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -116,6 +116,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { ElectionTicks: cfg.ElectionTicks(), AutoCompactionRetention: cfg.AutoCompactionRetention, QuotaBackendBytes: cfg.QuotaBackendBytes, + MaxTxnOps: cfg.MaxTxnOps, StrictReconfigCheck: cfg.StrictReconfigCheck, ClientCertAuthEnabled: cfg.ClientTLSInfo.ClientCertAuth, } diff --git a/etcdmain/config.go b/etcdmain/config.go index 4037ff76818..f95fd51bb6d 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -135,6 +135,7 @@ func newConfig() *config { fs.UintVar(&cfg.TickMs, "heartbeat-interval", cfg.TickMs, "Time (in milliseconds) of a heartbeat interval.") fs.UintVar(&cfg.ElectionMs, "election-timeout", cfg.ElectionMs, "Time (in milliseconds) for an election to timeout.") fs.Int64Var(&cfg.QuotaBackendBytes, "quota-backend-bytes", cfg.QuotaBackendBytes, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.") + fs.UintVar(&cfg.MaxTxnOps, "max-txn-ops", cfg.MaxTxnOps, "Maximum operations per txn that etcd server allows; defaults to 128.") // clustering fs.Var(flags.NewURLsValue(embed.DefaultInitialAdvertisePeerURLs), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster.") diff --git a/etcdmain/help.go b/etcdmain/help.go index 0ce4be636cc..d0bfead4d9f 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -60,6 +60,8 @@ member flags: comma-separated whitelist of origins for CORS (cross-origin resource sharing). --quota-backend-bytes '0' raise alarms when backend size exceeds the given quota (0 defaults to low space quota). + --max-txn-ops '128' + maximum operations per txn that etcd server allows; defaults to 128. clustering flags: diff --git a/etcdserver/api/v3rpc/key.go b/etcdserver/api/v3rpc/key.go index 6ea7bbacde0..a3e768cbad2 100644 --- a/etcdserver/api/v3rpc/key.go +++ b/etcdserver/api/v3rpc/key.go @@ -27,19 +27,20 @@ import ( var ( plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdserver/api/v3rpc") - - // Max operations per txn list. For example, Txn.Success can have at most 128 operations, - // and Txn.Failure can have at most 128 operations. - MaxOpsPerTxn = 128 ) type kvServer struct { hdr header kv etcdserver.RaftKV + // maxTxnOps is the max operations per txn. + // e.g suppose maxTxnOps = 128. + // Txn.Success can have at most 128 operations, + // and Txn.Failure can have at most 128 operations. + maxTxnOps uint } func NewKVServer(s *etcdserver.EtcdServer) pb.KVServer { - return &kvServer{hdr: newHeader(s), kv: s} + return &kvServer{hdr: newHeader(s), kv: s, maxTxnOps: s.Cfg.MaxTxnOps} } func (s *kvServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { @@ -94,7 +95,7 @@ func (s *kvServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (* } func (s *kvServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) { - if err := checkTxnRequest(r); err != nil { + if err := checkTxnRequest(r, int(s.maxTxnOps)); err != nil { return nil, err } @@ -144,8 +145,9 @@ func checkDeleteRequest(r *pb.DeleteRangeRequest) error { return nil } -func checkTxnRequest(r *pb.TxnRequest) error { - if len(r.Compare) > MaxOpsPerTxn || len(r.Success) > MaxOpsPerTxn || len(r.Failure) > MaxOpsPerTxn { +func checkTxnRequest(r *pb.TxnRequest, maxTxnOps int) error { + plog.Infof("maxTxnOps %v", maxTxnOps) + if len(r.Compare) > maxTxnOps || len(r.Success) > maxTxnOps || len(r.Failure) > maxTxnOps { return rpctypes.ErrGRPCTooManyOps } diff --git a/etcdserver/config.go b/etcdserver/config.go index 9bcac0f076b..9280702b1c8 100644 --- a/etcdserver/config.go +++ b/etcdserver/config.go @@ -54,6 +54,7 @@ type ServerConfig struct { AutoCompactionRetention int QuotaBackendBytes int64 + MaxTxnOps uint StrictReconfigCheck bool diff --git a/integration/v3_grpc_test.go b/integration/v3_grpc_test.go index 29f7ab193c1..715a63b7b78 100644 --- a/integration/v3_grpc_test.go +++ b/integration/v3_grpc_test.go @@ -187,7 +187,7 @@ func TestV3TxnTooManyOps(t *testing.T) { for i, tt := range tests { txn := &pb.TxnRequest{} - for j := 0; j < v3rpc.MaxOpsPerTxn+1; j++ { + for j := 0; j < v3rpc.MaxTxnOps+1; j++ { tt(txn) }