Skip to content

Commit b85ff6e

Browse files
author
Fabian Holler
committed
fix: *url.Errors are not detected as equal
Different instances of errors having the same struct values are unequal. That caused that *url.Errors with the same information did not evaluate as equale, compare the string represtation of the errors instead do determine equality.
1 parent 297d15a commit b85ff6e

4 files changed

Lines changed: 98 additions & 23 deletions

File tree

consul/resolver.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,11 @@ func (c *consulResolver) reportAddress(addrs []resolver.Address) bool {
283283
}
284284

285285
func (c *consulResolver) reportError(err error) bool {
286-
if c.lastReporterState.err == err { //nolint: errorlint
286+
// We compare the string representation of the errors because it is
287+
// simple and works. http.Client.Do() returns [*url.Error]s which are not
288+
// equal when compared with "==", neither [url.Error.Err] does because
289+
// it e.g. can contain a *net.OpError.
290+
if c.lastReporterState.err != nil && c.lastReporterState.err.Error() == err.Error() {
287291
return false
288292
}
289293

consul/resolver_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package consul
33
import (
44
"errors"
55
"fmt"
6+
"net"
67
"net/url"
78
"testing"
89
"time"
@@ -610,3 +611,59 @@ func TestRetryOnError(t *testing.T) {
610611
time.Sleep(50 * time.Millisecond)
611612
}
612613
}
614+
615+
func TestErrorsOnlyReportedOnce(t *testing.T) {
616+
var qe1 error = &url.Error{
617+
Op: "test",
618+
Err: &net.OpError{Op: "none"},
619+
}
620+
var qe2 error = &url.Error{
621+
Op: "test",
622+
Err: &net.OpError{Op: "none"},
623+
}
624+
625+
health := mocks.NewConsulHealthClient()
626+
health.SetRespError(qe1)
627+
health.ServiceMultipleTagsFn = func(c *mocks.ConsulHealthClient, _ string, _ []string, _ bool, _ *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {
628+
e1 := c.Err
629+
health.SetRespError(qe2)
630+
631+
c.Mutex.Lock()
632+
defer c.Mutex.Unlock()
633+
634+
c.ResolveCnt++
635+
636+
return nil, nil, e1
637+
}
638+
639+
cleanup := replaceCreateHealthClientFn(
640+
func(*consul.Config) (consulHealthEndpoint, error) {
641+
return health, nil
642+
},
643+
)
644+
t.Cleanup(cleanup)
645+
646+
cc := mocks.NewClientConn()
647+
b := NewBuilder()
648+
target := resolver.Target{URL: url.URL{Path: "user-service"}}
649+
650+
r, err := b.Build(target, cc, resolver.BuildOptions{})
651+
if err != nil {
652+
t.Fatal("Build() failed:", err.Error())
653+
}
654+
655+
r.ResolveNow(resolver.ResolveNowOptions{})
656+
for health.ResolveCount() < 1 {
657+
time.Sleep(time.Millisecond)
658+
}
659+
660+
r.ResolveNow(resolver.ResolveNowOptions{})
661+
662+
for health.ResolveCount() < 2 {
663+
time.Sleep(5 * time.Millisecond)
664+
}
665+
666+
if cc.ReportErrorCallCnt() != 1 {
667+
t.Errorf("ReportError was called %d times, expecting 1 call", cc.ReportErrorCallCnt())
668+
}
669+
}

internal/mocks/clientconn.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import (
99
)
1010

1111
type ClientConn struct {
12-
mutex sync.Mutex
13-
addrs []resolver.Address
14-
newAddressCallCnt int
15-
lastReportedError error
12+
mutex sync.Mutex
13+
addrs []resolver.Address
14+
newAddressCallCnt int
15+
reportErrorCallcnt int
16+
lastReportedError error
1617
}
1718

1819
func NewClientConn() *ClientConn {
@@ -27,6 +28,7 @@ func (t *ClientConn) ReportError(err error) {
2728
t.mutex.Lock()
2829
defer t.mutex.Unlock()
2930

31+
t.reportErrorCallcnt++
3032
t.lastReportedError = err
3133
}
3234

@@ -37,6 +39,13 @@ func (t *ClientConn) LastReportedError() error {
3739
return t.lastReportedError
3840
}
3941

42+
func (t *ClientConn) ReportErrorCallCnt() int {
43+
t.mutex.Lock()
44+
defer t.mutex.Unlock()
45+
46+
return t.reportErrorCallcnt
47+
}
48+
4049
func (t *ClientConn) UpdateState(state resolver.State) error {
4150
t.mutex.Lock()
4251
defer t.mutex.Unlock()

internal/mocks/consulhealth.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import (
77
)
88

99
type ConsulHealthClient struct {
10-
mutex sync.Mutex
11-
entries []*consul.ServiceEntry
12-
queryMeta consul.QueryMeta
13-
err error
14-
resolveCnt int
10+
Mutex sync.Mutex
11+
Entries []*consul.ServiceEntry
12+
queryMeta consul.QueryMeta
13+
ResolveCnt int
14+
Err error
15+
ServiceMultipleTagsFn func(*ConsulHealthClient, string, []string, bool, *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error)
1516
}
1617

1718
func NewConsulHealthClient() *ConsulHealthClient {
@@ -31,33 +32,37 @@ func (c *ConsulHealthClient) SetRespServiceEntries(s []*consul.AgentService) {
3132
}
3233

3334
func (c *ConsulHealthClient) SetRespEntries(entries []*consul.ServiceEntry) {
34-
c.mutex.Lock()
35-
defer c.mutex.Unlock()
35+
c.Mutex.Lock()
36+
defer c.Mutex.Unlock()
3637

37-
c.entries = entries
38+
c.Entries = entries
3839
}
3940

4041
func (c *ConsulHealthClient) SetRespError(err error) {
41-
c.mutex.Lock()
42-
defer c.mutex.Unlock()
42+
c.Mutex.Lock()
43+
defer c.Mutex.Unlock()
4344

44-
c.err = err
45+
c.Err = err
4546
}
4647

4748
func (c *ConsulHealthClient) ServiceMultipleTags(_ string, _ []string, _ bool, q *consul.QueryOptions) ([]*consul.ServiceEntry, *consul.QueryMeta, error) {
48-
c.mutex.Lock()
49-
defer c.mutex.Unlock()
50-
c.resolveCnt++
49+
if c.ServiceMultipleTagsFn != nil {
50+
return c.ServiceMultipleTagsFn(c, "", nil, false, q)
51+
}
52+
53+
c.Mutex.Lock()
54+
defer c.Mutex.Unlock()
55+
c.ResolveCnt++
5156

5257
if q.Context().Err() != nil {
5358
return nil, nil, q.Context().Err()
5459
}
5560

56-
return c.entries, &c.queryMeta, c.err
61+
return c.Entries, &c.queryMeta, c.Err
5762
}
5863

5964
func (c *ConsulHealthClient) ResolveCount() int {
60-
c.mutex.Lock()
61-
defer c.mutex.Unlock()
62-
return c.resolveCnt
65+
c.Mutex.Lock()
66+
defer c.Mutex.Unlock()
67+
return c.ResolveCnt
6368
}

0 commit comments

Comments
 (0)