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 }