-
Notifications
You must be signed in to change notification settings - Fork 1
/
shared.go
124 lines (106 loc) · 2.65 KB
/
shared.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
package sereno
import (
"fmt"
"log"
"math"
"math/rand"
"os"
"time"
etcdc "github.com/coreos/etcd/client"
"golang.org/x/net/context"
)
var CASErrorOutOfRetries error = fmt.Errorf("error trying to do a compare and swap of a value. out of retries.")
var DefaultTTL time.Duration = 24 * time.Hour
func dice() *rand.Rand {
return rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
}
func compareAndSwapUntil(ctx context.Context, tries int, keyid string, kapi etcdc.KeysAPI,
evaluator func(res *etcdc.Response, setOpts *etcdc.SetOptions) (val string, err error),
) error {
//uncomment for debugging..
id := int64(0)
if Usedtracedlogging {
id = dice().Int63()
}
for i := 0; i < tries; i++ {
resp, err := kapi.Get(ctx, keyid, &etcdc.GetOptions{Quorum: true})
if err != nil {
dlog("%v kapi get error %v", keyid, err)
return err
}
opt := &etcdc.SetOptions{}
nv, err := evaluator(resp, opt)
if err != nil {
dlog("%v eval error %v", keyid, err)
return err
}
dtrace("before: %v \tnewval:%v try:%v idx:%v key:%v", id, nv, i, resp.Index, keyid)
_, err = kapi.Set(ctx, keyid, nv, opt)
if err == nil {
dlog("%v update successful %v", keyid, err)
return nil
} else if !IsCompareAndSwapFailure(err) {
dlog("unexpected error %v", err)
return err
}
dtrace("after : %v \tnewval:%v try:%v key:%v error: %v", id, nv, i, keyid, err)
backoff(i)
}
return CASErrorOutOfRetries
}
func IsNodeExists(err error) bool {
if err != nil {
if errEtcd, ok := err.(etcdc.Error); ok {
if errEtcd.Code == etcdc.ErrorCodeNodeExist {
return true
}
}
}
return false
}
func IsKeyNotFound(err error) bool {
if err != nil {
if errEtcd, ok := err.(etcdc.Error); ok {
if errEtcd.Code == etcdc.ErrorCodeKeyNotFound {
return true
}
}
}
return false
}
func IsCompareAndSwapFailure(err error) bool {
if err != nil {
if errEtcd, ok := err.(etcdc.Error); ok {
if errEtcd.Code == etcdc.ErrorCodeTestFailed {
return true
}
}
}
return false
}
//backoff sleeps a random amount so we can.
//http://play.golang.org/p/l9aUHgiR8J
func backoff(try int) {
nf := math.Pow(4, float64(try))
nf = math.Max(1000, nf)
nf = math.Min(nf, 2000000)
r := dice().Int31n(int32(nf))
d := time.Duration(r) * time.Microsecond
time.Sleep(d)
}
//
// LOGGING
//
var UseDebugdlogging = false
var Usedtracedlogging = false
var std = log.New(os.StdErr, " ", log.Ltime|log.Lmicroseconds|log.Lshortfile)
func dlog(format string, v ...interface{}) {
if UseDebugdlogging {
std.Output(2, fmt.Sprintf(format, v...))
}
}
func dtrace(format string, v ...interface{}) {
if Usedtracedlogging {
std.Output(2, fmt.Sprintf(format, v...))
}
}