-
Notifications
You must be signed in to change notification settings - Fork 14
/
errors_collector.go
105 lines (86 loc) · 2.53 KB
/
errors_collector.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
Copyright 2020 YANDEX LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hasql
import (
"fmt"
"sort"
"strings"
"sync"
"time"
)
// CollectedErrors are errors collected when checking node statuses
type CollectedErrors struct {
Errors []NodeError
}
func (e *CollectedErrors) Error() string {
if len(e.Errors) == 1 {
return e.Errors[0].Error()
}
errs := make([]string, len(e.Errors))
for i, ne := range e.Errors {
errs[i] = ne.Error()
}
/*
I don't believe there exist 'best join separator' that fit all cases (cli output, JSON, .. etc),
so we use newline as error.Join did it.
In difficult cases (as suggested in https://github.com/yandex/go-hasql/pull/14),
the user should be able to receive "raw" errors and format them as it suits him.
*/
return strings.Join(errs, "\n")
}
// NodeError is error that background goroutine got while check given node
type NodeError struct {
Addr string
Err error
OccurredAt time.Time
}
func (e *NodeError) Error() string {
// 'foo.db' node error occurred at '2009-11-10..': FATAL: terminating connection due to ...
return fmt.Sprintf("%q node error occurred at %q: %s", e.Addr, e.OccurredAt, e.Err)
}
type errorsCollector struct {
store map[string]NodeError
mu sync.Mutex
}
func newErrorsCollector() errorsCollector {
return errorsCollector{store: make(map[string]NodeError)}
}
func (e *errorsCollector) Add(addr string, err error, occurredAt time.Time) {
e.mu.Lock()
defer e.mu.Unlock()
e.store[addr] = NodeError{
Addr: addr,
Err: err,
OccurredAt: occurredAt,
}
}
func (e *errorsCollector) Remove(addr string) {
e.mu.Lock()
defer e.mu.Unlock()
delete(e.store, addr)
}
func (e *errorsCollector) Err() error {
e.mu.Lock()
errList := make([]NodeError, 0, len(e.store))
for _, nErr := range e.store {
errList = append(errList, nErr)
}
e.mu.Unlock()
if len(errList) == 0 {
return nil
}
sort.Slice(errList, func(i, j int) bool {
return errList[i].OccurredAt.Before(errList[j].OccurredAt)
})
return &CollectedErrors{Errors: errList}
}