@@ -29,6 +29,7 @@ import (
29
29
"encoding/json"
30
30
"errors"
31
31
"fmt"
32
+ "net"
32
33
"sync"
33
34
34
35
"google.golang.org/grpc/balancer"
61
62
// TODO: change to pick-first when this becomes the default pick_first policy.
62
63
const logPrefix = "[pick-first-leaf-lb %p] "
63
64
65
+ type ipAddrFamily int
66
+
67
+ const (
68
+ // ipAddrFamilyUnknown represents strings that can't be parsed as an IP
69
+ // address.
70
+ ipAddrFamilyUnknown ipAddrFamily = iota
71
+ ipAddrFamilyV4
72
+ ipAddrFamilyV6
73
+ )
74
+
64
75
type pickfirstBuilder struct {}
65
76
66
77
func (pickfirstBuilder ) Build (cc balancer.ClientConn , _ balancer.BuildOptions ) balancer.Balancer {
@@ -206,9 +217,6 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState
206
217
// "Flatten the list by concatenating the ordered list of addresses for
207
218
// each of the endpoints, in order." - A61
208
219
for _ , endpoint := range endpoints {
209
- // "In the flattened list, interleave addresses from the two address
210
- // families, as per RFC-8305 section 4." - A61
211
- // TODO: support the above language.
212
220
newAddrs = append (newAddrs , endpoint .Addresses ... )
213
221
}
214
222
} else {
@@ -232,6 +240,8 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState
232
240
// SubConn multiple times in the same pass. We don't want this.
233
241
newAddrs = deDupAddresses (newAddrs )
234
242
243
+ newAddrs = interleaveAddresses (newAddrs )
244
+
235
245
// Since we have a new set of addresses, we are again at first pass.
236
246
b .firstPass = true
237
247
@@ -314,6 +324,67 @@ func deDupAddresses(addrs []resolver.Address) []resolver.Address {
314
324
return retAddrs
315
325
}
316
326
327
+ // interleaveAddresses interleaves addresses of both families (IPv4 and IPv6)
328
+ // as per RFC-8305 section 4.
329
+ // Whichever address family is first in the list is followed by an address of
330
+ // the other address family; that is, if the first address in the list is IPv6,
331
+ // then the first IPv4 address should be moved up in the list to be second in
332
+ // the list. It doesn't support configuring "First Address Family Count", i.e.
333
+ // there will always be a single member of the first address family at the
334
+ // beginning of the interleaved list.
335
+ // Addresses that are neither IPv4 nor IPv6 are treated as part of a third
336
+ // "unknown" family for interleaving.
337
+ // See: https://datatracker.ietf.org/doc/html/rfc8305#autoid-6
338
+ func interleaveAddresses (addrs []resolver.Address ) []resolver.Address {
339
+ familyAddrsMap := map [ipAddrFamily ][]resolver.Address {}
340
+ interleavingOrder := []ipAddrFamily {}
341
+ for _ , addr := range addrs {
342
+ family := addressFamily (addr .Addr )
343
+ if _ , found := familyAddrsMap [family ]; ! found {
344
+ interleavingOrder = append (interleavingOrder , family )
345
+ }
346
+ familyAddrsMap [family ] = append (familyAddrsMap [family ], addr )
347
+ }
348
+
349
+ interleavedAddrs := make ([]resolver.Address , 0 , len (addrs ))
350
+
351
+ for curFamilyIdx := 0 ; len (interleavedAddrs ) < len (addrs ); curFamilyIdx = (curFamilyIdx + 1 ) % len (interleavingOrder ) {
352
+ // Some IP types may have fewer addresses than others, so we look for
353
+ // the next type that has a remaining member to add to the interleaved
354
+ // list.
355
+ family := interleavingOrder [curFamilyIdx ]
356
+ remainingMembers := familyAddrsMap [family ]
357
+ if len (remainingMembers ) > 0 {
358
+ interleavedAddrs = append (interleavedAddrs , remainingMembers [0 ])
359
+ familyAddrsMap [family ] = remainingMembers [1 :]
360
+ }
361
+ }
362
+
363
+ return interleavedAddrs
364
+ }
365
+
366
+ // addressFamily returns the ipAddrFamily after parsing the address string.
367
+ // If the address isn't of the format "ip-address:port", it returns
368
+ // ipAddrFamilyUnknown. The address may be valid even if it's not an IP when
369
+ // using a resolver like passthrough where the address may be a hostname in
370
+ // some format that the dialer can resolve.
371
+ func addressFamily (address string ) ipAddrFamily {
372
+ // Parse the IP after removing the port.
373
+ host , _ , err := net .SplitHostPort (address )
374
+ if err != nil {
375
+ return ipAddrFamilyUnknown
376
+ }
377
+ ip := net .ParseIP (host )
378
+ switch {
379
+ case ip .To4 () != nil :
380
+ return ipAddrFamilyV4
381
+ case ip .To16 () != nil :
382
+ return ipAddrFamilyV6
383
+ default :
384
+ return ipAddrFamilyUnknown
385
+ }
386
+ }
387
+
317
388
// reconcileSubConnsLocked updates the active subchannels based on a new address
318
389
// list from the resolver. It does this by:
319
390
// - closing subchannels: any existing subchannels associated with addresses
0 commit comments