/
linearizability_sptp.go
143 lines (121 loc) · 3.6 KB
/
linearizability_sptp.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
Copyright (c) Facebook, Inc. and its affiliates.
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 linearizability
import (
"context"
"fmt"
"math"
"time"
"github.com/facebook/time/ptp/sptp/stats"
log "github.com/sirupsen/logrus"
)
// SPTPTestResult is what we get after the test run
type SPTPTestResult struct {
Config SPTPTestConfig
Offset float64
Error error
}
// Target returns value of server
func (tr SPTPTestResult) Target() string {
return tr.Config.Server
}
// Good check if the test passed
func (tr SPTPTestResult) Good() (bool, error) {
if tr.Error != nil {
return false, tr.Error
}
if math.Abs(tr.Offset) > float64(tr.Config.LinearizabilityTestMaxGMOffset.Nanoseconds()) {
return false, nil
}
return true, nil
}
// Explain provides plain text explanation of linearizability test result
func (tr SPTPTestResult) Explain() string {
msg := fmt.Sprintf("linearizability test against %q", tr.Config.Server)
good, err := tr.Good()
if good {
return fmt.Sprintf("%s passed", msg)
}
if err != nil {
return fmt.Sprintf("%s couldn't be completed because of error: %v", msg, tr.Error)
}
return fmt.Sprintf("%s failed because the offset %.2fns is > %v", msg, tr.Offset, tr.Config.LinearizabilityTestMaxGMOffset)
}
// Err returns an error value of the PTP4lTestResult
func (tr SPTPTestResult) Err() error {
return tr.Error
}
// SPTPTestConfig is a configuration for Tester
type SPTPTestConfig struct {
Server string
sptpurl string
LinearizabilityTestMaxGMOffset time.Duration
}
// Target sets the server to test
func (p *SPTPTestConfig) Target(server string) {
p.Server = server
}
// SPTPTester is basically a half of PTP unicast client
type SPTPTester struct {
cfg *SPTPTestConfig
// measurement result
result *SPTPTestResult
}
// NewSPTPTester initializes a Tester
func NewSPTPTester(server string, sptpurl string, linearizabilityTestMaxGMOffset time.Duration) (*SPTPTester, error) {
cfg := &SPTPTestConfig{
Server: server,
sptpurl: sptpurl,
LinearizabilityTestMaxGMOffset: linearizabilityTestMaxGMOffset,
}
t := &SPTPTester{
cfg: cfg,
}
return t, nil
}
// Close the connection
func (lt *SPTPTester) Close() error {
return nil
}
// RunTest performs one Tester run and will exit on completion.
// The result of the test will be returned, including any error arising during the test.
// Warning: the listener must be started via RunListener before calling this function.
func (lt *SPTPTester) RunTest(_ context.Context) TestResult {
result := SPTPTestResult{
Config: *lt.cfg,
Error: nil,
}
log.Debugf("test starting %s", lt.cfg.Server)
stats, err := stats.FetchStats(lt.cfg.sptpurl)
if err != nil {
result.Error = err
return result
}
found := false
for _, s := range stats {
if s.GMAddress == lt.cfg.Server {
result.Offset = s.Offset
if s.Error != "" {
result.Error = fmt.Errorf(s.Error)
}
found = true
break
}
}
if !found {
result.Error = fmt.Errorf("failed to find offset results for %s ", lt.cfg.Server)
}
lt.result = &result
log.Debugf("test done %s", lt.cfg.Server)
return result
}