func (r *Replicator) Write(keys []string, request []byte, operation string, fopts *forward.Options,
	opts *Options) (responses []Response, err error) {
	opts = mergeDefaultOptions(opts, r.defaults)
	return r.readWrite(write, keys, request, operation, fopts, opts)
}
Write replicates a write request. It takes key(s) to be used for lookup of the requests
destination, a request to send, the operation to perform at the destination, options
for forwarding the request as well as options for ffanning out the request. It also
takes a response type, which is the type of struct that will be returned in each
responses.Body in response. Response type must be a concrete struct. The body field
will contain a pointer to that type of struct.
package replica
import (
	"errors"
	"sync"
	log "github.com/uber-common/bark"
	"github.com/uber/ringpop-go/forward"
	"github.com/uber/ringpop-go/logging"
	"github.com/uber/ringpop-go/shared"
	"github.com/uber/ringpop-go/util"
	"github.com/uber/tchannel-go"
)
type FanoutMode int
const (
	Parallel FanoutMode = iota
	SerialSequential
	SerialBalanced
)
const (
	read int = iota
	write
)
type Sender interface {
	Lookup(string) (string, error)
	LookupN(string, int) ([]string, error)
	WhoAmI() (string, error)
}
type Response struct {
	Destination string
	Keys        []string
	Body        []byte
}
type Options struct {
	NValue, RValue, WValue int
	FanoutMode             FanoutMode
}
type callOptions struct {
	Keys       []string
	Dests      []string
	Request    []byte
	KeysByDest map[string][]string
	Operation  string
	Format     tchannel.Format
}
type Replicator struct {
	sender    Sender
	channel   shared.SubChannel
	forwarder *forward.Forwarder
	logger    log.Logger
	defaults  *Options
}
func selectFanoutMode(mode FanoutMode) FanoutMode {
	switch mode {
	case Parallel, SerialSequential, SerialBalanced:
		return mode
	default:
		return Parallel
	}
}
func mergeDefaultOptions(opts *Options, def *Options) *Options {
	if opts == nil {
		return def
	}
	var merged Options
	merged.NValue = util.SelectInt(opts.NValue, def.NValue)
	merged.RValue = util.SelectInt(opts.RValue, def.RValue)
	merged.WValue = util.SelectInt(opts.WValue, def.WValue)
	merged.FanoutMode = selectFanoutMode(opts.FanoutMode)
	return &merged
}
func NewReplicator(s Sender, channel shared.SubChannel, logger log.Logger,
	opts *Options) *Replicator {
	f := forward.NewForwarder(s, channel)
	opts = mergeDefaultOptions(opts, &Options{3, 1, 3, Parallel})
	logger = logging.Logger("replicator")
	if address, err := s.WhoAmI(); err == nil {
		logger = logger.WithField("local", address)
	}
	return &Replicator{s, channel, f, logger, opts}
}
func (r *Replicator) Read(keys []string, request []byte, operation string, fopts *forward.Options,
	opts *Options) (responses []Response, err error) {
	opts = mergeDefaultOptions(opts, r.defaults)
	return r.readWrite(read, keys, request, operation, fopts, opts)
}