github.com/thanos-io/thanos@v0.32.5/pkg/targets/targets.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package targets
     5  
     6  import (
     7  	"context"
     8  	"sort"
     9  	"sync"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/prometheus/prometheus/storage"
    13  	"github.com/thanos-io/thanos/pkg/store/storepb"
    14  	"github.com/thanos-io/thanos/pkg/targets/targetspb"
    15  )
    16  
    17  var _ UnaryClient = &GRPCClient{}
    18  
    19  // UnaryClient is gRPC targetspb.Targets client which expands streaming targets API. Useful for consumers that does not
    20  // support streaming.
    21  type UnaryClient interface {
    22  	Targets(ctx context.Context, req *targetspb.TargetsRequest) (*targetspb.TargetDiscovery, storage.Warnings, error)
    23  }
    24  
    25  // GRPCClient allows to retrieve targets from local gRPC streaming server implementation.
    26  // TODO(bwplotka): Switch to native gRPC transparent client->server adapter once available.
    27  type GRPCClient struct {
    28  	proxy targetspb.TargetsServer
    29  
    30  	replicaLabels map[string]struct{}
    31  }
    32  
    33  func NewGRPCClient(ts targetspb.TargetsServer) *GRPCClient {
    34  	return NewGRPCClientWithDedup(ts, nil)
    35  }
    36  
    37  func NewGRPCClientWithDedup(ts targetspb.TargetsServer, replicaLabels []string) *GRPCClient {
    38  	c := &GRPCClient{
    39  		proxy:         ts,
    40  		replicaLabels: map[string]struct{}{},
    41  	}
    42  
    43  	for _, label := range replicaLabels {
    44  		c.replicaLabels[label] = struct{}{}
    45  	}
    46  	return c
    47  }
    48  
    49  func (rr *GRPCClient) Targets(ctx context.Context, req *targetspb.TargetsRequest) (*targetspb.TargetDiscovery, storage.Warnings, error) {
    50  	resp := &targetsServer{ctx: ctx, targets: &targetspb.TargetDiscovery{
    51  		ActiveTargets:  make([]*targetspb.ActiveTarget, 0),
    52  		DroppedTargets: make([]*targetspb.DroppedTarget, 0),
    53  	}}
    54  
    55  	if err := rr.proxy.Targets(req, resp); err != nil {
    56  		return nil, nil, errors.Wrap(err, "proxy Targets")
    57  	}
    58  
    59  	resp.targets = dedupTargets(resp.targets, rr.replicaLabels)
    60  
    61  	return resp.targets, resp.warnings, nil
    62  }
    63  
    64  // dedupTargets re-sorts the set so that the same target with different replica
    65  // labels are coming right after each other.
    66  func dedupTargets(targets *targetspb.TargetDiscovery, replicaLabels map[string]struct{}) *targetspb.TargetDiscovery {
    67  	if targets == nil {
    68  		return nil
    69  	}
    70  
    71  	targets.ActiveTargets = dedupActiveTargets(targets.ActiveTargets, replicaLabels)
    72  	targets.DroppedTargets = dedupDroppedTargets(targets.DroppedTargets, replicaLabels)
    73  
    74  	return targets
    75  }
    76  
    77  func dedupDroppedTargets(droppedTargets []*targetspb.DroppedTarget, replicaLabels map[string]struct{}) []*targetspb.DroppedTarget {
    78  	if len(droppedTargets) == 0 {
    79  		return droppedTargets
    80  	}
    81  
    82  	// Sort each target's label names such that they are comparable.
    83  	for _, t := range droppedTargets {
    84  		sort.Slice(t.DiscoveredLabels.Labels, func(i, j int) bool {
    85  			return t.DiscoveredLabels.Labels[i].Name < t.DiscoveredLabels.Labels[j].Name
    86  		})
    87  	}
    88  
    89  	// Sort targets globally based on synthesized deduplication labels, also considering replica labels and their values.
    90  	sort.Slice(droppedTargets, func(i, j int) bool {
    91  		return droppedTargets[i].Compare(droppedTargets[j]) < 0
    92  	})
    93  
    94  	// Remove targets based on synthesized deduplication labels, this time ignoring replica labels
    95  	i := 0
    96  	droppedTargets[i].DiscoveredLabels.Labels = removeReplicaLabels(
    97  		droppedTargets[i].DiscoveredLabels.Labels,
    98  		replicaLabels,
    99  	)
   100  	for j := 1; j < len(droppedTargets); j++ {
   101  		droppedTargets[j].DiscoveredLabels.Labels = removeReplicaLabels(
   102  			droppedTargets[j].DiscoveredLabels.Labels,
   103  			replicaLabels,
   104  		)
   105  		if droppedTargets[i].Compare(droppedTargets[j]) != 0 {
   106  			// Effectively retain targets[j] in the resulting slice.
   107  			i++
   108  			droppedTargets[i] = droppedTargets[j]
   109  			continue
   110  		}
   111  	}
   112  
   113  	return droppedTargets[:i+1]
   114  }
   115  
   116  func dedupActiveTargets(activeTargets []*targetspb.ActiveTarget, replicaLabels map[string]struct{}) []*targetspb.ActiveTarget {
   117  	if len(activeTargets) == 0 {
   118  		return activeTargets
   119  	}
   120  
   121  	// Sort each target's label names such that they are comparable.
   122  	for _, t := range activeTargets {
   123  		sort.Slice(t.DiscoveredLabels.Labels, func(i, j int) bool {
   124  			return t.DiscoveredLabels.Labels[i].Name < t.DiscoveredLabels.Labels[j].Name
   125  		})
   126  	}
   127  
   128  	// Sort targets globally based on synthesized deduplication labels, also considering replica labels and their values.
   129  	sort.Slice(activeTargets, func(i, j int) bool {
   130  		return activeTargets[i].Compare(activeTargets[j]) < 0
   131  	})
   132  
   133  	// Remove targets based on synthesized deduplication labels, this time ignoring replica labels and last scrape.
   134  	i := 0
   135  	activeTargets[i].DiscoveredLabels.Labels = removeReplicaLabels(
   136  		activeTargets[i].DiscoveredLabels.Labels,
   137  		replicaLabels,
   138  	)
   139  	activeTargets[i].Labels.Labels = removeReplicaLabels(
   140  		activeTargets[i].Labels.Labels,
   141  		replicaLabels,
   142  	)
   143  	for j := 1; j < len(activeTargets); j++ {
   144  		activeTargets[j].DiscoveredLabels.Labels = removeReplicaLabels(
   145  			activeTargets[j].DiscoveredLabels.Labels,
   146  			replicaLabels,
   147  		)
   148  		activeTargets[j].Labels.Labels = removeReplicaLabels(
   149  			activeTargets[j].Labels.Labels,
   150  			replicaLabels,
   151  		)
   152  
   153  		if activeTargets[i].Compare(activeTargets[j]) != 0 {
   154  			// Effectively retain targets[j] in the resulting slice.
   155  			i++
   156  			activeTargets[i] = activeTargets[j]
   157  			continue
   158  		}
   159  
   160  		if activeTargets[i].CompareState(activeTargets[j]) <= 0 {
   161  			continue
   162  		}
   163  
   164  		// Swap if we found a younger target.
   165  		activeTargets[i] = activeTargets[j]
   166  	}
   167  
   168  	return activeTargets[:i+1]
   169  }
   170  
   171  func removeReplicaLabels(labels []storepb.Label, replicaLabels map[string]struct{}) []storepb.Label {
   172  	newLabels := make([]storepb.Label, 0, len(labels))
   173  	for _, l := range labels {
   174  		if _, ok := replicaLabels[l.Name]; !ok {
   175  			newLabels = append(newLabels, l)
   176  		}
   177  	}
   178  
   179  	return newLabels
   180  }
   181  
   182  type targetsServer struct {
   183  	// This field just exist to pseudo-implement the unused methods of the interface.
   184  	targetspb.Targets_TargetsServer
   185  	ctx context.Context
   186  
   187  	warnings []error
   188  	targets  *targetspb.TargetDiscovery
   189  	mu       sync.Mutex
   190  }
   191  
   192  func (srv *targetsServer) Send(res *targetspb.TargetsResponse) error {
   193  	if res.GetWarning() != "" {
   194  		srv.mu.Lock()
   195  		defer srv.mu.Unlock()
   196  		srv.warnings = append(srv.warnings, errors.New(res.GetWarning()))
   197  		return nil
   198  	}
   199  
   200  	if res.GetTargets() == nil {
   201  		return errors.New("no targets")
   202  	}
   203  	srv.mu.Lock()
   204  	defer srv.mu.Unlock()
   205  	srv.targets.ActiveTargets = append(srv.targets.ActiveTargets, res.GetTargets().ActiveTargets...)
   206  	srv.targets.DroppedTargets = append(srv.targets.DroppedTargets, res.GetTargets().DroppedTargets...)
   207  
   208  	return nil
   209  }
   210  
   211  func (srv *targetsServer) Context() context.Context {
   212  	return srv.ctx
   213  }