forked from TrueCloudLab/frostfs-node
155 lines
4.3 KiB
Go
155 lines
4.3 KiB
Go
|
package replication
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"time"
|
||
|
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// ObjectLocationDetector is an interface of entity
|
||
|
// that listens tasks to detect object current locations in network.
|
||
|
ObjectLocationDetector interface {
|
||
|
Process(ctx context.Context) chan<- Address
|
||
|
Subscribe(ch chan<- *ObjectLocationRecord)
|
||
|
}
|
||
|
|
||
|
objectLocationDetector struct {
|
||
|
weightComparator WeightComparator
|
||
|
objectLocator ObjectLocator
|
||
|
reservationRatioReceiver ReservationRatioReceiver
|
||
|
presenceChecker PresenceChecker
|
||
|
log *zap.Logger
|
||
|
|
||
|
taskChanCap int
|
||
|
resultTimeout time.Duration
|
||
|
resultChan chan<- *ObjectLocationRecord
|
||
|
}
|
||
|
|
||
|
// LocationDetectorParams groups the parameters of location detector's constructor.
|
||
|
LocationDetectorParams struct {
|
||
|
WeightComparator
|
||
|
ObjectLocator
|
||
|
ReservationRatioReceiver
|
||
|
PresenceChecker
|
||
|
*zap.Logger
|
||
|
|
||
|
TaskChanCap int
|
||
|
ResultTimeout time.Duration
|
||
|
}
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
defaultLocationDetectorChanCap = 10
|
||
|
defaultLocationDetectorResultTimeout = time.Second
|
||
|
locationDetectorEntity = "object location detector"
|
||
|
)
|
||
|
|
||
|
func (s *objectLocationDetector) Subscribe(ch chan<- *ObjectLocationRecord) { s.resultChan = ch }
|
||
|
|
||
|
func (s *objectLocationDetector) Process(ctx context.Context) chan<- Address {
|
||
|
ch := make(chan Address, s.taskChanCap)
|
||
|
go s.processRoutine(ctx, ch)
|
||
|
|
||
|
return ch
|
||
|
}
|
||
|
|
||
|
func (s *objectLocationDetector) writeResult(locationRecord *ObjectLocationRecord) {
|
||
|
if s.resultChan == nil {
|
||
|
return
|
||
|
}
|
||
|
select {
|
||
|
case s.resultChan <- locationRecord:
|
||
|
case <-time.After(s.resultTimeout):
|
||
|
s.log.Warn(writeResultTimeout)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *objectLocationDetector) processRoutine(ctx context.Context, taskChan <-chan Address) {
|
||
|
loop:
|
||
|
for {
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
s.log.Warn(locationDetectorEntity+ctxDoneMsg, zap.Error(ctx.Err()))
|
||
|
break loop
|
||
|
case addr, ok := <-taskChan:
|
||
|
if !ok {
|
||
|
s.log.Warn(locationDetectorEntity + taskChanClosed)
|
||
|
break loop
|
||
|
} else if has, err := s.presenceChecker.Has(addr); err != nil || !has {
|
||
|
continue loop
|
||
|
}
|
||
|
s.handleTask(ctx, addr)
|
||
|
}
|
||
|
}
|
||
|
close(s.resultChan)
|
||
|
}
|
||
|
|
||
|
func (s *objectLocationDetector) handleTask(ctx context.Context, addr Address) {
|
||
|
var (
|
||
|
err error
|
||
|
log = s.log.With(addressFields(addr)...)
|
||
|
locationRecord = &ObjectLocationRecord{addr, 0, nil}
|
||
|
)
|
||
|
|
||
|
if locationRecord.ReservationRatio, err = s.reservationRatioReceiver.ReservationRatio(ctx, addr); err != nil {
|
||
|
log.Error("reservation ratio computation failure", zap.Error(err))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
nodes, err := s.objectLocator.LocateObject(ctx, addr)
|
||
|
if err != nil {
|
||
|
log.Error("locate object failure", zap.Error(err))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for i := range nodes {
|
||
|
locationRecord.Locations = append(locationRecord.Locations, ObjectLocation{
|
||
|
Node: nodes[i],
|
||
|
WeightGreater: s.weightComparator.CompareWeight(ctx, addr, nodes[i]) == 1,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
log.Debug("current location record created",
|
||
|
zap.Int("reservation ratio", locationRecord.ReservationRatio),
|
||
|
zap.Any("storage nodes exclude self", locationRecord.Locations))
|
||
|
|
||
|
s.writeResult(locationRecord)
|
||
|
}
|
||
|
|
||
|
// NewLocationDetector is an object location detector's constructor.
|
||
|
func NewLocationDetector(p *LocationDetectorParams) (ObjectLocationDetector, error) {
|
||
|
switch {
|
||
|
case p.PresenceChecker == nil:
|
||
|
return nil, instanceError(locationDetectorEntity, presenceCheckerPart)
|
||
|
case p.ObjectLocator == nil:
|
||
|
return nil, instanceError(locationDetectorEntity, objectLocatorPart)
|
||
|
case p.ReservationRatioReceiver == nil:
|
||
|
return nil, instanceError(locationDetectorEntity, reservationRatioReceiverPart)
|
||
|
case p.Logger == nil:
|
||
|
return nil, instanceError(locationDetectorEntity, loggerPart)
|
||
|
case p.WeightComparator == nil:
|
||
|
return nil, instanceError(locationDetectorEntity, weightComparatorPart)
|
||
|
}
|
||
|
|
||
|
if p.TaskChanCap <= 0 {
|
||
|
p.TaskChanCap = defaultLocationDetectorChanCap
|
||
|
}
|
||
|
|
||
|
if p.ResultTimeout <= 0 {
|
||
|
p.ResultTimeout = defaultLocationDetectorResultTimeout
|
||
|
}
|
||
|
|
||
|
return &objectLocationDetector{
|
||
|
weightComparator: p.WeightComparator,
|
||
|
objectLocator: p.ObjectLocator,
|
||
|
reservationRatioReceiver: p.ReservationRatioReceiver,
|
||
|
presenceChecker: p.PresenceChecker,
|
||
|
log: p.Logger,
|
||
|
taskChanCap: p.TaskChanCap,
|
||
|
resultTimeout: p.ResultTimeout,
|
||
|
resultChan: nil,
|
||
|
}, nil
|
||
|
}
|