mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
server: Add --listen-client-http-urls flag to allow running grpc server separate from http server
Difference in load configuration for watch delay tests show how huge the impact is. Even with random write scheduler grpc under http server can only handle 500 KB with 2 seconds delay. On the other hand, separate grpc server easily hits 10, 100 or even 1000 MB within 100 miliseconds. Priority write scheduler that was used in most previous releases is far worse than random one. Tests configured to only 5 MB to avoid flakes and taking too long to fill etcd. Signed-off-by: Marek Siarkowicz <siarkowicz@google.com>
This commit is contained in:
@@ -205,12 +205,12 @@ type Config struct {
|
|||||||
// streams that each client can open at a time.
|
// streams that each client can open at a time.
|
||||||
MaxConcurrentStreams uint32 `json:"max-concurrent-streams"`
|
MaxConcurrentStreams uint32 `json:"max-concurrent-streams"`
|
||||||
|
|
||||||
ListenPeerUrls, ListenClientUrls []url.URL
|
ListenPeerUrls, ListenClientUrls, ListenClientHttpUrls []url.URL
|
||||||
AdvertisePeerUrls, AdvertiseClientUrls []url.URL
|
AdvertisePeerUrls, AdvertiseClientUrls []url.URL
|
||||||
ClientTLSInfo transport.TLSInfo
|
ClientTLSInfo transport.TLSInfo
|
||||||
ClientAutoTLS bool
|
ClientAutoTLS bool
|
||||||
PeerTLSInfo transport.TLSInfo
|
PeerTLSInfo transport.TLSInfo
|
||||||
PeerAutoTLS bool
|
PeerAutoTLS bool
|
||||||
// SelfSignedCertValidity specifies the validity period of the client and peer certificates
|
// SelfSignedCertValidity specifies the validity period of the client and peer certificates
|
||||||
// that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS,
|
// that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS,
|
||||||
// the unit is year, and the default is 1
|
// the unit is year, and the default is 1
|
||||||
@@ -423,10 +423,11 @@ type configYAML struct {
|
|||||||
|
|
||||||
// configJSON has file options that are translated into Config options
|
// configJSON has file options that are translated into Config options
|
||||||
type configJSON struct {
|
type configJSON struct {
|
||||||
ListenPeerUrls string `json:"listen-peer-urls"`
|
ListenPeerUrls string `json:"listen-peer-urls"`
|
||||||
ListenClientUrls string `json:"listen-client-urls"`
|
ListenClientUrls string `json:"listen-client-urls"`
|
||||||
AdvertisePeerUrls string `json:"initial-advertise-peer-urls"`
|
ListenClientHttpUrls string `json:"listen-client-http-urls"`
|
||||||
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
AdvertisePeerUrls string `json:"initial-advertise-peer-urls"`
|
||||||
|
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
||||||
|
|
||||||
CORSJSON string `json:"cors"`
|
CORSJSON string `json:"cors"`
|
||||||
HostWhitelistJSON string `json:"host-whitelist"`
|
HostWhitelistJSON string `json:"host-whitelist"`
|
||||||
@@ -557,6 +558,15 @@ func (cfg *configYAML) configFromFile(path string) error {
|
|||||||
cfg.Config.ListenClientUrls = u
|
cfg.Config.ListenClientUrls = u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.configJSON.ListenClientHttpUrls != "" {
|
||||||
|
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientHttpUrls, ","))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-http-urls: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
cfg.Config.ListenClientHttpUrls = u
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.configJSON.AdvertisePeerUrls != "" {
|
if cfg.configJSON.AdvertisePeerUrls != "" {
|
||||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertisePeerUrls, ","))
|
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertisePeerUrls, ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -656,6 +666,12 @@ func (cfg *Config) Validate() error {
|
|||||||
if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
|
if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(cfg.ListenClientHttpUrls) == 0 {
|
||||||
|
cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
|
||||||
|
}
|
||||||
if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
|
if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -877,9 +893,12 @@ func (cfg *Config) ClientSelfCert() (err error) {
|
|||||||
cfg.logger.Warn("ignoring client auto TLS since certs given")
|
cfg.logger.Warn("ignoring client auto TLS since certs given")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
chosts := make([]string, len(cfg.ListenClientUrls))
|
chosts := make([]string, 0, len(cfg.ListenClientUrls)+len(cfg.ListenClientHttpUrls))
|
||||||
for i, u := range cfg.ListenClientUrls {
|
for _, u := range cfg.ListenClientUrls {
|
||||||
chosts[i] = u.Host
|
chosts = append(chosts, u.Host)
|
||||||
|
}
|
||||||
|
for _, u := range cfg.ListenClientHttpUrls {
|
||||||
|
chosts = append(chosts, u.Host)
|
||||||
}
|
}
|
||||||
cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts, cfg.SelfSignedCertValidity)
|
cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts, cfg.SelfSignedCertValidity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1014,6 +1033,14 @@ func (cfg *Config) getListenClientUrls() (ss []string) {
|
|||||||
return ss
|
return ss
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) getListenClientHttpUrls() (ss []string) {
|
||||||
|
ss = make([]string, len(cfg.ListenClientHttpUrls))
|
||||||
|
for i := range cfg.ListenClientHttpUrls {
|
||||||
|
ss[i] = cfg.ListenClientHttpUrls[i].String()
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *Config) getMetricsURLs() (ss []string) {
|
func (cfg *Config) getMetricsURLs() (ss []string) {
|
||||||
ss = make([]string, len(cfg.ListenMetricsUrls))
|
ss = make([]string, len(cfg.ListenMetricsUrls))
|
||||||
for i := range cfg.ListenMetricsUrls {
|
for i := range cfg.ListenMetricsUrls {
|
||||||
|
|||||||
@@ -440,11 +440,16 @@ func (e *Etcd) Close() {
|
|||||||
|
|
||||||
func stopServers(ctx context.Context, ss *servers) {
|
func stopServers(ctx context.Context, ss *servers) {
|
||||||
// first, close the http.Server
|
// first, close the http.Server
|
||||||
ss.http.Shutdown(ctx)
|
if ss.http != nil {
|
||||||
// do not grpc.Server.GracefulStop with TLS enabled etcd server
|
ss.http.Shutdown(ctx)
|
||||||
|
}
|
||||||
|
if ss.grpc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// do not grpc.Server.GracefulStop when grpc runs under http server
|
||||||
// See https://github.com/grpc/grpc-go/issues/1384#issuecomment-317124531
|
// See https://github.com/grpc/grpc-go/issues/1384#issuecomment-317124531
|
||||||
// and https://github.com/etcd-io/etcd/issues/8916
|
// and https://github.com/etcd-io/etcd/issues/8916
|
||||||
if ss.secure {
|
if ss.secure && ss.http != nil {
|
||||||
ss.grpc.Stop()
|
ss.grpc.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -614,7 +619,7 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
sctxs = make(map[string]*serveCtx)
|
sctxs = make(map[string]*serveCtx)
|
||||||
for _, u := range cfg.ListenClientUrls {
|
for _, u := range append(cfg.ListenClientUrls, cfg.ListenClientHttpUrls...) {
|
||||||
if u.Scheme == "http" || u.Scheme == "unix" {
|
if u.Scheme == "http" || u.Scheme == "unix" {
|
||||||
if !cfg.ClientTLSInfo.Empty() {
|
if !cfg.ClientTLSInfo.Empty() {
|
||||||
cfg.logger.Warn("scheme is HTTP while key and cert files are present; ignoring key and cert files", zap.String("client-url", u.String()))
|
cfg.logger.Warn("scheme is HTTP while key and cert files are present; ignoring key and cert files", zap.String("client-url", u.String()))
|
||||||
@@ -641,6 +646,24 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||||||
sctx.addr = addr
|
sctx.addr = addr
|
||||||
sctx.network = network
|
sctx.network = network
|
||||||
}
|
}
|
||||||
|
for _, u := range cfg.ListenClientHttpUrls {
|
||||||
|
addr, secure, network := resolveUrl(u)
|
||||||
|
|
||||||
|
sctx := sctxs[addr]
|
||||||
|
if sctx == nil {
|
||||||
|
sctx = newServeCtx(cfg.logger)
|
||||||
|
sctxs[addr] = sctx
|
||||||
|
} else if !sctx.httpOnly {
|
||||||
|
return nil, fmt.Errorf("cannot bind both --client-listen-urls and --client-listen-http-urls on the same url %s", u.String())
|
||||||
|
}
|
||||||
|
sctx.secure = sctx.secure || secure
|
||||||
|
sctx.insecure = sctx.insecure || !secure
|
||||||
|
sctx.scheme = u.Scheme
|
||||||
|
sctx.addr = addr
|
||||||
|
sctx.network = network
|
||||||
|
sctx.httpOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
for _, sctx := range sctxs {
|
for _, sctx := range sctxs {
|
||||||
if sctx.l, err = transport.NewListenerWithOpts(sctx.addr, sctx.scheme,
|
if sctx.l, err = transport.NewListenerWithOpts(sctx.addr, sctx.scheme,
|
||||||
transport.WithSocketOpts(&cfg.SocketOpts),
|
transport.WithSocketOpts(&cfg.SocketOpts),
|
||||||
@@ -663,7 +686,7 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer func(addr string) {
|
defer func(addr string) {
|
||||||
if err == nil {
|
if err == nil || sctx.l == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sctx.l.Close()
|
sctx.l.Close()
|
||||||
@@ -743,20 +766,27 @@ func (e *Etcd) serveClients() (err error) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
splitHttp := false
|
||||||
|
for _, sctx := range e.sctxs {
|
||||||
|
if sctx.httpOnly {
|
||||||
|
splitHttp = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start client servers in each goroutine
|
// start client servers in each goroutine
|
||||||
for _, sctx := range e.sctxs {
|
for _, sctx := range e.sctxs {
|
||||||
go func(s *serveCtx) {
|
go func(s *serveCtx) {
|
||||||
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, e.grpcGatewayDial(), gopts...))
|
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, e.grpcGatewayDial(splitHttp), splitHttp, gopts...))
|
||||||
}(sctx)
|
}(sctx)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Etcd) grpcGatewayDial() (grpcDial func(ctx context.Context) (*grpc.ClientConn, error)) {
|
func (e *Etcd) grpcGatewayDial(splitHttp bool) (grpcDial func(ctx context.Context) (*grpc.ClientConn, error)) {
|
||||||
if !e.cfg.EnableGRPCGateway {
|
if !e.cfg.EnableGRPCGateway {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sctx := e.pickGrpcGatewayServeContext()
|
sctx := e.pickGrpcGatewayServeContext(splitHttp)
|
||||||
addr := sctx.addr
|
addr := sctx.addr
|
||||||
if network := sctx.network; network == "unix" {
|
if network := sctx.network; network == "unix" {
|
||||||
// explicitly define unix network for gRPC socket support
|
// explicitly define unix network for gRPC socket support
|
||||||
@@ -790,9 +820,11 @@ func (e *Etcd) grpcGatewayDial() (grpcDial func(ctx context.Context) (*grpc.Clie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Etcd) pickGrpcGatewayServeContext() *serveCtx {
|
func (e *Etcd) pickGrpcGatewayServeContext(splitHttp bool) *serveCtx {
|
||||||
for _, sctx := range e.sctxs {
|
for _, sctx := range e.sctxs {
|
||||||
return sctx
|
if !splitHttp || !sctx.httpOnly {
|
||||||
|
return sctx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("Expect at least one context able to serve grpc")
|
panic("Expect at least one context able to serve grpc")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ type serveCtx struct {
|
|||||||
network string
|
network string
|
||||||
secure bool
|
secure bool
|
||||||
insecure bool
|
insecure bool
|
||||||
|
httpOnly bool
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
@@ -94,6 +95,7 @@ func (sctx *serveCtx) serve(
|
|||||||
handler http.Handler,
|
handler http.Handler,
|
||||||
errHandler func(error),
|
errHandler func(error),
|
||||||
grpcDialForRestGatewayBackends func(ctx context.Context) (*grpc.ClientConn, error),
|
grpcDialForRestGatewayBackends func(ctx context.Context) (*grpc.ClientConn, error),
|
||||||
|
splitHttp bool,
|
||||||
gopts ...grpc.ServerOption) (err error) {
|
gopts ...grpc.ServerOption) (err error) {
|
||||||
logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0)
|
logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0)
|
||||||
<-s.ReadyNotify()
|
<-s.ReadyNotify()
|
||||||
@@ -101,6 +103,12 @@ func (sctx *serveCtx) serve(
|
|||||||
sctx.lg.Info("ready to serve client requests")
|
sctx.lg.Info("ready to serve client requests")
|
||||||
|
|
||||||
m := cmux.New(sctx.l)
|
m := cmux.New(sctx.l)
|
||||||
|
var server func() error
|
||||||
|
onlyGRPC := splitHttp && !sctx.httpOnly
|
||||||
|
onlyHttp := splitHttp && sctx.httpOnly
|
||||||
|
grpcEnabled := !onlyHttp
|
||||||
|
httpEnabled := !onlyGRPC
|
||||||
|
|
||||||
v3c := v3client.New(s)
|
v3c := v3client.New(s)
|
||||||
servElection := v3election.NewElectionServer(v3c)
|
servElection := v3election.NewElectionServer(v3c)
|
||||||
servLock := v3lock.NewLockServer(v3c)
|
servLock := v3lock.NewLockServer(v3c)
|
||||||
@@ -116,104 +124,137 @@ func (sctx *serveCtx) serve(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var traffic string
|
||||||
|
switch {
|
||||||
|
case onlyGRPC:
|
||||||
|
traffic = "grpc"
|
||||||
|
case onlyHttp:
|
||||||
|
traffic = "http"
|
||||||
|
default:
|
||||||
|
traffic = "grpc+http"
|
||||||
|
}
|
||||||
|
|
||||||
if sctx.insecure {
|
if sctx.insecure {
|
||||||
gs := v3rpc.Server(s, nil, nil, gopts...)
|
var gs *grpc.Server
|
||||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
var srv *http.Server
|
||||||
v3lockpb.RegisterLockServer(gs, servLock)
|
if httpEnabled {
|
||||||
if sctx.serviceRegister != nil {
|
httpmux := sctx.createMux(gwmux, handler)
|
||||||
sctx.serviceRegister(gs)
|
srv = &http.Server{
|
||||||
}
|
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||||
|
ErrorLog: logger, // do not log user error
|
||||||
defer func(gs *grpc.Server) {
|
}
|
||||||
if err != nil {
|
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||||
sctx.lg.Warn("stopping insecure grpc server due to error", zap.Error(err))
|
sctx.lg.Error("Configure http server failed", zap.Error(err))
|
||||||
gs.Stop()
|
return err
|
||||||
sctx.lg.Warn("stopped insecure grpc server due to error", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
}(gs)
|
|
||||||
|
|
||||||
grpcl := m.Match(cmux.HTTP2())
|
|
||||||
go func(gs *grpc.Server, grpcLis net.Listener) {
|
|
||||||
errHandler(gs.Serve(grpcLis))
|
|
||||||
}(gs, grpcl)
|
|
||||||
|
|
||||||
httpmux := sctx.createMux(gwmux, handler)
|
|
||||||
|
|
||||||
srvhttp := &http.Server{
|
|
||||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
|
||||||
ErrorLog: logger, // do not log user error
|
|
||||||
}
|
}
|
||||||
if err := configureHttpServer(srvhttp, s.Cfg); err != nil {
|
if grpcEnabled {
|
||||||
sctx.lg.Error("Configure http server failed", zap.Error(err))
|
gs = v3rpc.Server(s, nil, nil, gopts...)
|
||||||
return err
|
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||||
|
v3lockpb.RegisterLockServer(gs, servLock)
|
||||||
|
if sctx.serviceRegister != nil {
|
||||||
|
sctx.serviceRegister(gs)
|
||||||
|
}
|
||||||
|
defer func(gs *grpc.Server) {
|
||||||
|
if err != nil {
|
||||||
|
sctx.lg.Warn("stopping insecure grpc server due to error", zap.Error(err))
|
||||||
|
gs.Stop()
|
||||||
|
sctx.lg.Warn("stopped insecure grpc server due to error", zap.Error(err))
|
||||||
|
}
|
||||||
|
}(gs)
|
||||||
}
|
}
|
||||||
httpl := m.Match(cmux.HTTP1())
|
if onlyGRPC {
|
||||||
|
server = func() error {
|
||||||
|
return gs.Serve(sctx.l)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server = m.Serve
|
||||||
|
|
||||||
go func(srvhttp *http.Server, httpLis net.Listener) {
|
httpl := m.Match(cmux.HTTP1())
|
||||||
errHandler(srvhttp.Serve(httpLis))
|
go func(srvhttp *http.Server, tlsLis net.Listener) {
|
||||||
}(srvhttp, httpl)
|
errHandler(srvhttp.Serve(tlsLis))
|
||||||
|
}(srv, httpl)
|
||||||
|
|
||||||
sctx.serversC <- &servers{grpc: gs, http: srvhttp}
|
if grpcEnabled {
|
||||||
|
grpcl := m.Match(cmux.HTTP2())
|
||||||
|
go func(gs *grpc.Server, l net.Listener) {
|
||||||
|
errHandler(gs.Serve(l))
|
||||||
|
}(gs, grpcl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sctx.serversC <- &servers{grpc: gs, http: srv}
|
||||||
sctx.lg.Info(
|
sctx.lg.Info(
|
||||||
"serving client traffic insecurely; this is strongly discouraged!",
|
"serving client traffic insecurely; this is strongly discouraged!",
|
||||||
|
zap.String("traffic", traffic),
|
||||||
zap.String("address", sctx.l.Addr().String()),
|
zap.String("address", sctx.l.Addr().String()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sctx.secure {
|
if sctx.secure {
|
||||||
|
var gs *grpc.Server
|
||||||
|
var srv *http.Server
|
||||||
|
|
||||||
tlscfg, tlsErr := tlsinfo.ServerConfig()
|
tlscfg, tlsErr := tlsinfo.ServerConfig()
|
||||||
if tlsErr != nil {
|
if tlsErr != nil {
|
||||||
return tlsErr
|
return tlsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
gs := v3rpc.Server(s, tlscfg, nil, gopts...)
|
if grpcEnabled {
|
||||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
|
||||||
v3lockpb.RegisterLockServer(gs, servLock)
|
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||||
if sctx.serviceRegister != nil {
|
v3lockpb.RegisterLockServer(gs, servLock)
|
||||||
sctx.serviceRegister(gs)
|
if sctx.serviceRegister != nil {
|
||||||
}
|
sctx.serviceRegister(gs)
|
||||||
|
|
||||||
defer func(gs *grpc.Server) {
|
|
||||||
if err != nil {
|
|
||||||
sctx.lg.Warn("stopping secure grpc server due to error", zap.Error(err))
|
|
||||||
gs.Stop()
|
|
||||||
sctx.lg.Warn("stopped secure grpc server due to error", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
}(gs)
|
defer func(gs *grpc.Server) {
|
||||||
|
if err != nil {
|
||||||
handler = grpcHandlerFunc(gs, handler)
|
sctx.lg.Warn("stopping secure grpc server due to error", zap.Error(err))
|
||||||
var tlsl net.Listener
|
gs.Stop()
|
||||||
tlsl, err = transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
sctx.lg.Warn("stopped secure grpc server due to error", zap.Error(err))
|
||||||
if err != nil {
|
}
|
||||||
return err
|
}(gs)
|
||||||
}
|
}
|
||||||
// TODO: add debug flag; enable logging when debug flag is set
|
if httpEnabled {
|
||||||
httpmux := sctx.createMux(gwmux, handler)
|
if grpcEnabled {
|
||||||
|
handler = grpcHandlerFunc(gs, handler)
|
||||||
|
}
|
||||||
|
httpmux := sctx.createMux(gwmux, handler)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv = &http.Server{
|
||||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||||
TLSConfig: tlscfg,
|
TLSConfig: tlscfg,
|
||||||
ErrorLog: logger, // do not log user error
|
ErrorLog: logger, // do not log user error
|
||||||
}
|
}
|
||||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||||
sctx.lg.Error("Configure https server failed", zap.Error(err))
|
sctx.lg.Error("Configure https server failed", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(srvhttp *http.Server, tlsLis net.Listener) {
|
if onlyGRPC {
|
||||||
errHandler(srvhttp.Serve(tlsLis))
|
server = func() error { return gs.Serve(sctx.l) }
|
||||||
}(srv, tlsl)
|
} else {
|
||||||
|
server = m.Serve
|
||||||
|
|
||||||
|
tlsl, err := transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func(srvhttp *http.Server, tlsl net.Listener) {
|
||||||
|
errHandler(srvhttp.Serve(tlsl))
|
||||||
|
}(srv, tlsl)
|
||||||
|
}
|
||||||
|
|
||||||
sctx.serversC <- &servers{secure: true, grpc: gs, http: srv}
|
sctx.serversC <- &servers{secure: true, grpc: gs, http: srv}
|
||||||
sctx.lg.Info(
|
sctx.lg.Info(
|
||||||
"serving client traffic securely",
|
"serving client traffic securely",
|
||||||
|
zap.String("traffic", traffic),
|
||||||
zap.String("address", sctx.l.Addr().String()),
|
zap.String("address", sctx.l.Addr().String()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sctx.serversC)
|
return server()
|
||||||
return m.Serve()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureHttpServer(srv *http.Server, cfg config.ServerConfig) error {
|
func configureHttpServer(srv *http.Server, cfg config.ServerConfig) error {
|
||||||
|
|||||||
@@ -146,7 +146,11 @@ func newConfig() *config {
|
|||||||
)
|
)
|
||||||
fs.Var(
|
fs.Var(
|
||||||
flags.NewUniqueURLsWithExceptions(embed.DefaultListenClientURLs, ""), "listen-client-urls",
|
flags.NewUniqueURLsWithExceptions(embed.DefaultListenClientURLs, ""), "listen-client-urls",
|
||||||
"List of URLs to listen on for client traffic.",
|
"List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.",
|
||||||
|
)
|
||||||
|
fs.Var(
|
||||||
|
flags.NewUniqueURLsWithExceptions("", ""), "listen-client-http-urls",
|
||||||
|
"List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.",
|
||||||
)
|
)
|
||||||
fs.Var(
|
fs.Var(
|
||||||
flags.NewUniqueURLsWithExceptions("", ""),
|
flags.NewUniqueURLsWithExceptions("", ""),
|
||||||
@@ -395,6 +399,7 @@ func (cfg *config) configFromCmdLine() error {
|
|||||||
cfg.ec.ListenPeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
|
cfg.ec.ListenPeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
|
||||||
cfg.ec.AdvertisePeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
|
cfg.ec.AdvertisePeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
|
||||||
cfg.ec.ListenClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
|
cfg.ec.ListenClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
|
||||||
|
cfg.ec.ListenClientHttpUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-http-urls")
|
||||||
cfg.ec.AdvertiseClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
|
cfg.ec.AdvertiseClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
|
||||||
cfg.ec.ListenMetricsUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-metrics-urls")
|
cfg.ec.ListenMetricsUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-metrics-urls")
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
|||||||
"-snapshot-count=10",
|
"-snapshot-count=10",
|
||||||
"-listen-peer-urls=http://localhost:8000,https://localhost:8001",
|
"-listen-peer-urls=http://localhost:8000,https://localhost:8001",
|
||||||
"-listen-client-urls=http://localhost:7000,https://localhost:7001",
|
"-listen-client-urls=http://localhost:7000,https://localhost:7001",
|
||||||
|
"-listen-client-http-urls=http://localhost:7002,https://localhost:7003",
|
||||||
// it should be set if -listen-client-urls is set
|
// it should be set if -listen-client-urls is set
|
||||||
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
||||||
}
|
}
|
||||||
@@ -51,14 +52,15 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
|||||||
|
|
||||||
func TestConfigFileMemberFields(t *testing.T) {
|
func TestConfigFileMemberFields(t *testing.T) {
|
||||||
yc := struct {
|
yc := struct {
|
||||||
Dir string `json:"data-dir"`
|
Dir string `json:"data-dir"`
|
||||||
MaxSnapFiles uint `json:"max-snapshots"`
|
MaxSnapFiles uint `json:"max-snapshots"`
|
||||||
MaxWalFiles uint `json:"max-wals"`
|
MaxWalFiles uint `json:"max-wals"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
SnapshotCount uint64 `json:"snapshot-count"`
|
SnapshotCount uint64 `json:"snapshot-count"`
|
||||||
ListenPeerUrls string `json:"listen-peer-urls"`
|
ListenPeerUrls string `json:"listen-peer-urls"`
|
||||||
ListenClientUrls string `json:"listen-client-urls"`
|
ListenClientUrls string `json:"listen-client-urls"`
|
||||||
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
ListenClientHttpUrls string `json:"listen-client-http-urls"`
|
||||||
|
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
||||||
}{
|
}{
|
||||||
"testdir",
|
"testdir",
|
||||||
10,
|
10,
|
||||||
@@ -67,6 +69,7 @@ func TestConfigFileMemberFields(t *testing.T) {
|
|||||||
10,
|
10,
|
||||||
"http://localhost:8000,https://localhost:8001",
|
"http://localhost:8000,https://localhost:8001",
|
||||||
"http://localhost:7000,https://localhost:7001",
|
"http://localhost:7000,https://localhost:7001",
|
||||||
|
"http://localhost:7002,https://localhost:7003",
|
||||||
"http://localhost:7000,https://localhost:7001",
|
"http://localhost:7000,https://localhost:7001",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,13 +516,14 @@ func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
|
|||||||
|
|
||||||
func validateMemberFlags(t *testing.T, cfg *config) {
|
func validateMemberFlags(t *testing.T, cfg *config) {
|
||||||
wcfg := &embed.Config{
|
wcfg := &embed.Config{
|
||||||
Dir: "testdir",
|
Dir: "testdir",
|
||||||
ListenPeerUrls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
ListenPeerUrls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
||||||
ListenClientUrls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
ListenClientUrls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
||||||
MaxSnapFiles: 10,
|
ListenClientHttpUrls: []url.URL{{Scheme: "http", Host: "localhost:7002"}, {Scheme: "https", Host: "localhost:7003"}},
|
||||||
MaxWalFiles: 10,
|
MaxSnapFiles: 10,
|
||||||
Name: "testname",
|
MaxWalFiles: 10,
|
||||||
SnapshotCount: 10,
|
Name: "testname",
|
||||||
|
SnapshotCount: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.ec.Dir != wcfg.Dir {
|
if cfg.ec.Dir != wcfg.Dir {
|
||||||
@@ -543,6 +547,9 @@ func validateMemberFlags(t *testing.T, cfg *config) {
|
|||||||
if !reflect.DeepEqual(cfg.ec.ListenClientUrls, wcfg.ListenClientUrls) {
|
if !reflect.DeepEqual(cfg.ec.ListenClientUrls, wcfg.ListenClientUrls) {
|
||||||
t.Errorf("listen-client-urls = %v, want %v", cfg.ec.ListenClientUrls, wcfg.ListenClientUrls)
|
t.Errorf("listen-client-urls = %v, want %v", cfg.ec.ListenClientUrls, wcfg.ListenClientUrls)
|
||||||
}
|
}
|
||||||
|
if !reflect.DeepEqual(cfg.ec.ListenClientHttpUrls, wcfg.ListenClientHttpUrls) {
|
||||||
|
t.Errorf("listen-client-http-urls = %v, want %v", cfg.ec.ListenClientHttpUrls, wcfg.ListenClientHttpUrls)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateClusteringFlags(t *testing.T, cfg *config) {
|
func validateClusteringFlags(t *testing.T, cfg *config) {
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ Member:
|
|||||||
--listen-peer-urls 'http://localhost:2380'
|
--listen-peer-urls 'http://localhost:2380'
|
||||||
List of URLs to listen on for peer traffic.
|
List of URLs to listen on for peer traffic.
|
||||||
--listen-client-urls 'http://localhost:2379'
|
--listen-client-urls 'http://localhost:2379'
|
||||||
List of URLs to listen on for client traffic.
|
List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.
|
||||||
|
--listen-client-http-urls ''
|
||||||
|
List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.
|
||||||
--max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
|
--max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
|
||||||
Maximum number of snapshot files to retain (0 is unlimited).
|
Maximum number of snapshot files to retain (0 is unlimited).
|
||||||
--max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
|
--max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ type etcdProcessClusterConfig struct {
|
|||||||
|
|
||||||
clientTLS clientConnType
|
clientTLS clientConnType
|
||||||
clientCertAuthEnabled bool
|
clientCertAuthEnabled bool
|
||||||
|
clientHttpSeparate bool
|
||||||
isPeerTLS bool
|
isPeerTLS bool
|
||||||
isPeerAutoTLS bool
|
isPeerAutoTLS bool
|
||||||
isClientAutoTLS bool
|
isClientAutoTLS bool
|
||||||
@@ -247,6 +248,7 @@ func (cfg *etcdProcessClusterConfig) etcdServerProcessConfigs(tb testing.TB) []*
|
|||||||
var curls []string
|
var curls []string
|
||||||
var curl, curltls string
|
var curl, curltls string
|
||||||
port := cfg.basePort + 5*i
|
port := cfg.basePort + 5*i
|
||||||
|
clientHttpPort := port + 4
|
||||||
curlHost := fmt.Sprintf("localhost:%d", port)
|
curlHost := fmt.Sprintf("localhost:%d", port)
|
||||||
|
|
||||||
switch cfg.clientTLS {
|
switch cfg.clientTLS {
|
||||||
@@ -277,6 +279,10 @@ func (cfg *etcdProcessClusterConfig) etcdServerProcessConfigs(tb testing.TB) []*
|
|||||||
"--data-dir", dataDirPath,
|
"--data-dir", dataDirPath,
|
||||||
"--snapshot-count", fmt.Sprintf("%d", cfg.snapshotCount),
|
"--snapshot-count", fmt.Sprintf("%d", cfg.snapshotCount),
|
||||||
}
|
}
|
||||||
|
if cfg.clientHttpSeparate {
|
||||||
|
clientHttpUrl := url.URL{Scheme: cfg.clientScheme(), Host: fmt.Sprintf("localhost:%d", clientHttpPort)}
|
||||||
|
args = append(args, "--listen-client-http-urls", clientHttpUrl.String())
|
||||||
|
}
|
||||||
args = addV2Args(args)
|
args = addV2Args(args)
|
||||||
if cfg.forceNewCluster {
|
if cfg.forceNewCluster {
|
||||||
args = append(args, "--force-new-cluster")
|
args = append(args, "--force-new-cluster")
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ func (ctl *Etcdctl) cmdArgs(args ...string) []string {
|
|||||||
func (ctl *Etcdctl) flags() map[string]string {
|
func (ctl *Etcdctl) flags() map[string]string {
|
||||||
fmap := make(map[string]string)
|
fmap := make(map[string]string)
|
||||||
if ctl.v2 {
|
if ctl.v2 {
|
||||||
|
fmap["no-sync"] = "true"
|
||||||
if ctl.connType == clientTLS {
|
if ctl.connType == clientTLS {
|
||||||
fmap["ca-file"] = integration.TestTLSInfo.TrustedCAFile
|
fmap["ca-file"] = integration.TestTLSInfo.TrustedCAFile
|
||||||
fmap["cert-file"] = integration.TestTLSInfo.CertFile
|
fmap["cert-file"] = integration.TestTLSInfo.CertFile
|
||||||
|
|||||||
@@ -101,15 +101,17 @@ func tlsInfo(t testing.TB, connType clientConnType, isAutoTLS bool) (*transport.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillEtcdWithData(ctx context.Context, c *clientv3.Client, keyCount int, valueSize uint) error {
|
func fillEtcdWithData(ctx context.Context, c *clientv3.Client, dbSize int) error {
|
||||||
g := errgroup.Group{}
|
g := errgroup.Group{}
|
||||||
concurrency := 10
|
concurrency := 10
|
||||||
|
keyCount := 100
|
||||||
keysPerRoutine := keyCount / concurrency
|
keysPerRoutine := keyCount / concurrency
|
||||||
|
valueSize := dbSize / keyCount
|
||||||
for i := 0; i < concurrency; i++ {
|
for i := 0; i < concurrency; i++ {
|
||||||
i := i
|
i := i
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
for j := 0; j < keysPerRoutine; j++ {
|
for j := 0; j < keysPerRoutine; j++ {
|
||||||
_, err := c.Put(ctx, fmt.Sprintf("%d", i*keysPerRoutine+j), stringutil.RandString(valueSize))
|
_, err := c.Put(ctx, fmt.Sprintf("%d", i*keysPerRoutine+j), stringutil.RandString(uint(valueSize)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,29 +33,48 @@ import (
|
|||||||
const (
|
const (
|
||||||
watchResponsePeriod = 100 * time.Millisecond
|
watchResponsePeriod = 100 * time.Millisecond
|
||||||
watchTestDuration = 5 * time.Second
|
watchTestDuration = 5 * time.Second
|
||||||
// TODO: Reduce maxWatchDelay when https://github.com/etcd-io/etcd/issues/15402 is addressed.
|
readLoadConcurrency = 10
|
||||||
maxWatchDelay = 2 * time.Second
|
|
||||||
// Configure enough read load to cause starvation from https://github.com/etcd-io/etcd/issues/15402.
|
|
||||||
// Tweaked to pass on GitHub runner. For local runs please increase parameters.
|
|
||||||
// TODO: Increase when https://github.com/etcd-io/etcd/issues/15402 is fully addressed.
|
|
||||||
numberOfPreexistingKeys = 100
|
|
||||||
sizeOfPreexistingValues = 5000
|
|
||||||
readLoadConcurrency = 10
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
config etcdProcessClusterConfig
|
config etcdProcessClusterConfig
|
||||||
|
maxWatchDelay time.Duration
|
||||||
|
dbSizeBytes int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Kilo = 1000
|
||||||
|
Mega = 1000 * Kilo
|
||||||
|
)
|
||||||
|
|
||||||
|
// 10 MB is not a bottleneck of grpc server, but filling up etcd with data.
|
||||||
|
// Keeping it lower so tests don't take too long.
|
||||||
|
// If we implement reuse of db we could increase the dbSize.
|
||||||
var tcs = []testCase{
|
var tcs = []testCase{
|
||||||
{
|
{
|
||||||
name: "NoTLS",
|
name: "NoTLS",
|
||||||
config: etcdProcessClusterConfig{clusterSize: 1},
|
config: etcdProcessClusterConfig{clusterSize: 1},
|
||||||
|
maxWatchDelay: 100 * time.Millisecond,
|
||||||
|
dbSizeBytes: 5 * Mega,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ClientTLS",
|
name: "TLS",
|
||||||
config: etcdProcessClusterConfig{clusterSize: 1, isClientAutoTLS: true, clientTLS: clientTLS},
|
config: etcdProcessClusterConfig{clusterSize: 1, isClientAutoTLS: true, clientTLS: clientTLS},
|
||||||
|
maxWatchDelay: 2 * time.Second,
|
||||||
|
dbSizeBytes: 500 * Kilo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SeparateHttpNoTLS",
|
||||||
|
config: etcdProcessClusterConfig{clusterSize: 1, clientHttpSeparate: true},
|
||||||
|
maxWatchDelay: 100 * time.Millisecond,
|
||||||
|
dbSizeBytes: 5 * Mega,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SeparateHttpTLS",
|
||||||
|
config: etcdProcessClusterConfig{clusterSize: 1, isClientAutoTLS: true, clientTLS: clientTLS, clientHttpSeparate: true},
|
||||||
|
maxWatchDelay: 100 * time.Millisecond,
|
||||||
|
dbSizeBytes: 5 * Mega,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +88,13 @@ func TestWatchDelayForPeriodicProgressNotification(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer clus.Close()
|
defer clus.Close()
|
||||||
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
||||||
require.NoError(t, fillEtcdWithData(context.Background(), c, numberOfPreexistingKeys, sizeOfPreexistingValues))
|
require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
g := errgroup.Group{}
|
g := errgroup.Group{}
|
||||||
continuouslyExecuteGetAll(ctx, t, &g, c)
|
continuouslyExecuteGetAll(ctx, t, &g, c)
|
||||||
validateWatchDelay(t, c.Watch(ctx, "fake-key", clientv3.WithProgressNotify()))
|
validateWatchDelay(t, c.Watch(ctx, "fake-key", clientv3.WithProgressNotify()), tc.maxWatchDelay)
|
||||||
require.NoError(t, g.Wait())
|
require.NoError(t, g.Wait())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -89,7 +108,7 @@ func TestWatchDelayForManualProgressNotification(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer clus.Close()
|
defer clus.Close()
|
||||||
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
||||||
require.NoError(t, fillEtcdWithData(context.Background(), c, numberOfPreexistingKeys, sizeOfPreexistingValues))
|
require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -107,7 +126,7 @@ func TestWatchDelayForManualProgressNotification(t *testing.T) {
|
|||||||
time.Sleep(watchResponsePeriod)
|
time.Sleep(watchResponsePeriod)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
validateWatchDelay(t, c.Watch(ctx, "fake-key"))
|
validateWatchDelay(t, c.Watch(ctx, "fake-key"), tc.maxWatchDelay)
|
||||||
require.NoError(t, g.Wait())
|
require.NoError(t, g.Wait())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -121,7 +140,7 @@ func TestWatchDelayForEvent(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer clus.Close()
|
defer clus.Close()
|
||||||
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
c := newClient(t, clus.EndpointsV3(), tc.config.clientTLS, tc.config.isClientAutoTLS)
|
||||||
require.NoError(t, fillEtcdWithData(context.Background(), c, numberOfPreexistingKeys, sizeOfPreexistingValues))
|
require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -140,13 +159,13 @@ func TestWatchDelayForEvent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
continuouslyExecuteGetAll(ctx, t, &g, c)
|
continuouslyExecuteGetAll(ctx, t, &g, c)
|
||||||
validateWatchDelay(t, c.Watch(ctx, "key"))
|
validateWatchDelay(t, c.Watch(ctx, "key"), tc.maxWatchDelay)
|
||||||
require.NoError(t, g.Wait())
|
require.NoError(t, g.Wait())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateWatchDelay(t *testing.T, watch clientv3.WatchChan) {
|
func validateWatchDelay(t *testing.T, watch clientv3.WatchChan, maxWatchDelay time.Duration) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
var maxDelay time.Duration
|
var maxDelay time.Duration
|
||||||
for range watch {
|
for range watch {
|
||||||
@@ -177,15 +196,19 @@ func continuouslyExecuteGetAll(ctx context.Context, t *testing.T, g *errgroup.Gr
|
|||||||
for i := 0; i < readLoadConcurrency; i++ {
|
for i := 0; i < readLoadConcurrency; i++ {
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
for {
|
for {
|
||||||
_, err := c.Get(ctx, "", clientv3.WithPrefix())
|
resp, err := c.Get(ctx, "", clientv3.WithPrefix())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "context deadline exceeded") {
|
if strings.Contains(err.Error(), "context deadline exceeded") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
respSize := 0
|
||||||
|
for _, kv := range resp.Kvs {
|
||||||
|
respSize += kv.Size()
|
||||||
|
}
|
||||||
mux.Lock()
|
mux.Lock()
|
||||||
size += numberOfPreexistingKeys * sizeOfPreexistingValues
|
size += respSize
|
||||||
mux.Unlock()
|
mux.Unlock()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user