Skip to content

Commit 2d372a8

Browse files
Allow splitting reads/writes when load balancing
In addition to specifying one or more `--target` servers to deploy, you can now also specify one or more `--read-target`. When any read targets are present, traffic will be routed such that read requests go to read targets, and write requests go to write targets. A read request is defined as a `GET` or `HEAD`, and excludes WebSocket traffic. Everything else is considered to be a write request. Read requests that immediately follow a write on the same session will also be treated as writes. In configurations where readers are replicas of writers, this helps avoid problems where replication lag leads to a client failing to read an item it just wrote. This "writer affinity" is controlled by a cookie, and by default the effect lasts for 3 seconds. It can be adjusted via `--writer-affinity-timeout`, or disabled entirely by setting the timeout to zero. Aside from this writer affinity case, writers will not receive any read requests unless they are also explicitly listed as a `--read-target`
1 parent 3ab342c commit 2d372a8

13 files changed

+495
-239
lines changed

internal/cmd/deploy.go

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func newDeployCommand() *deployCommand {
2828
}
2929

3030
deployCommand.cmd.Flags().StringSliceVar(&deployCommand.args.TargetURLs, "target", []string{}, "Target host(s) to deploy")
31+
deployCommand.cmd.Flags().StringSliceVar(&deployCommand.args.ReaderURLs, "read-target", []string{}, "Read-only target host(s) to deploy")
3132
deployCommand.cmd.Flags().StringSliceVar(&deployCommand.args.ServiceOptions.Hosts, "host", []string{}, "Host(s) to serve this target on (empty for wildcard)")
3233
deployCommand.cmd.Flags().StringSliceVar(&deployCommand.args.ServiceOptions.PathPrefixes, "path-prefix", []string{}, "Deploy the service below the specified path(s)")
3334
deployCommand.cmd.Flags().BoolVar(&deployCommand.args.ServiceOptions.StripPrefix, "strip-path-prefix", true, "With --path-prefix, strip prefix from request before forwarding")
@@ -43,6 +44,7 @@ func newDeployCommand() *deployCommand {
4344
deployCommand.cmd.Flags().DurationVar(&deployCommand.args.TargetOptions.HealthCheckConfig.Interval, "health-check-interval", server.DefaultHealthCheckInterval, "Interval between health checks")
4445
deployCommand.cmd.Flags().DurationVar(&deployCommand.args.TargetOptions.HealthCheckConfig.Timeout, "health-check-timeout", server.DefaultHealthCheckTimeout, "Time each health check must complete in")
4546
deployCommand.cmd.Flags().StringVar(&deployCommand.args.TargetOptions.HealthCheckConfig.Path, "health-check-path", server.DefaultHealthCheckPath, "Path to check for health")
47+
deployCommand.cmd.Flags().DurationVar(&deployCommand.args.ServiceOptions.WriterAffinityTimeout, "writer-affinity-timeout", server.DefaultWriterAffinityTimeout, "Time after a write before read requests will be routed to readers")
4648

4749
deployCommand.cmd.Flags().DurationVar(&deployCommand.args.TargetOptions.ResponseTimeout, "target-timeout", server.DefaultTargetTimeout, "Maximum time to wait for the target server to respond when serving requests")
4850

internal/cmd/rollout_deploy.go

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func newRolloutDeployCommand() *rolloutDeployCommand {
2323
}
2424

2525
rolloutDeployCommand.cmd.Flags().StringSliceVar(&rolloutDeployCommand.args.TargetURLs, "target", []string{}, "Target host(s) to deploy")
26+
rolloutDeployCommand.cmd.Flags().StringSliceVar(&rolloutDeployCommand.args.ReaderURLs, "read-target", []string{}, "Read-only target host(s) to deploy")
2627
rolloutDeployCommand.cmd.Flags().DurationVar(&rolloutDeployCommand.args.DeployTimeout, "deploy-timeout", server.DefaultDeployTimeout, "Maximum time to wait for the new target to become healthy")
2728
rolloutDeployCommand.cmd.Flags().DurationVar(&rolloutDeployCommand.args.DrainTimeout, "drain-timeout", server.DefaultDrainTimeout, "Maximum time to allow existing connections to drain before removing old target")
2829

internal/server/commands.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type CommandHandler struct {
1919
type DeployArgs struct {
2020
Service string
2121
TargetURLs []string
22+
ReaderURLs []string
2223
DeployTimeout time.Duration
2324
DrainTimeout time.Duration
2425
ServiceOptions ServiceOptions
@@ -48,6 +49,7 @@ type RemoveArgs struct {
4849
type RolloutDeployArgs struct {
4950
Service string
5051
TargetURLs []string
52+
ReaderURLs []string
5153
DeployTimeout time.Duration
5254
DrainTimeout time.Duration
5355
}
@@ -113,7 +115,7 @@ func (h *CommandHandler) Close() error {
113115
}
114116

115117
func (h *CommandHandler) Deploy(args DeployArgs, reply *bool) error {
116-
return h.router.DeployService(args.Service, args.TargetURLs, args.ServiceOptions, args.TargetOptions, args.DeployTimeout, args.DrainTimeout)
118+
return h.router.DeployService(args.Service, args.TargetURLs, args.ReaderURLs, args.ServiceOptions, args.TargetOptions, args.DeployTimeout, args.DrainTimeout)
117119
}
118120

119121
func (h *CommandHandler) Pause(args PauseArgs, reply *bool) error {
@@ -139,7 +141,7 @@ func (h *CommandHandler) List(args bool, reply *ListResponse) error {
139141
}
140142

141143
func (h *CommandHandler) RolloutDeploy(args RolloutDeployArgs, reply *bool) error {
142-
return h.router.SetRolloutTargets(args.Service, args.TargetURLs, args.DeployTimeout, args.DrainTimeout)
144+
return h.router.SetRolloutTargets(args.Service, args.TargetURLs, args.ReaderURLs, args.DeployTimeout, args.DrainTimeout)
143145
}
144146

145147
func (h *CommandHandler) RolloutSet(args RolloutSetArgs, reply *bool) error {

internal/server/health_check.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const (
1616
)
1717

1818
var (
19-
ErrorHealthCheckRequestTimedOut = errors.New("Request timed out")
20-
ErrorHealthCheckUnexpectedStatus = errors.New("Unexpected status")
19+
ErrorHealthCheckRequestTimedOut = errors.New("request timed out")
20+
ErrorHealthCheckUnexpectedStatus = errors.New("unexpected status")
2121
)
2222

2323
type HealthCheckConsumer interface {

0 commit comments

Comments
 (0)