Skip to content
File

Blob: spec/chord/errors.go

go88 lines
1package chord
2 
3import (
4 "context"
5 "errors"
6 
7 "github.com/twitchtv/twirp"
8)
9 
10type Error struct {
11 msg string
12}
13 
14func (e *Error) Error() string {
15 return e.msg
16}
17 
18var (
19 ErrJoinInvalidState = errorDef("chord/membership: node cannot handle join request at the moment", true)
20 ErrJoinTransferFailure = errorDef("chord/membership: failed to transfer keys to joiner node", true)
21 ErrJoinInvalidSuccessor = errorDef("chord/membership: join request was routed to the wrong successor node", true)
22 ErrLeaveInvalidState = errorDef("chord/membership: node cannot handle leave request at the moment", true)
23 ErrLeaveTransferFailure = errorDef("chord/membership: failed to transfer keys to successor node", true)
24 ErrKVStaleOwnership = errorDef("chord/kv: processing node no longer has ownership over requested key", true)
25 ErrKVPendingTransfer = errorDef("chord/kv: kv transfer inprogress, state may be outdated", true)
26 
27 ErrNodeGone = errorDef("chord: node is not part of the chord ring", false)
28 ErrNodeNotStarted = errorDef("chord: node is not running", false)
29 ErrNodeNoSuccessor = errorDef("chord: node has no successor, possibly invalid chord ring", false)
30 ErrNodeNil = errorDef("chord: node cannot be nil", false)
31 ErrDuplicateJoinerID = errorDef("chord/membership: joining node has duplicate ID as its successor", false)
32 ErrKVSimpleConflict = errorDef("chord/kv: simple key was concurrently modified", false)
33 ErrKVPrefixConflict = errorDef("chord/kv: child already exists under prefix", false)
34 ErrKVLeaseConflict = errorDef("chord/kv: lease has not expired or was acquired by a different requester", false)
35 ErrKVLeaseExpired = errorDef("chord/kv: lease has expired with the given token", false)
36 ErrKVLeaseInvalidTTL = errorDef("chord/kv: lease ttl must be greater than a second", false)
37 ErrKVHashFnChanged = errorDef("chord/kv: calculated hash is different from storage", false)
38)
39 
40func ErrorIsRetryable(err error) bool {
41 for _, e := range retryableErrs {
42 if errors.Is(err, e) {
43 return true
44 }
45 }
46 return false
47}
48 
49// this is needed because RPC call squash type information, so in call site with signature
50// if err == ErrABC will fail (but err.Error() == ErrABC.Error() will work).
51func ErrorMapper(err error) error {
52 if err == nil {
53 return err
54 }
55 
56 var (
57 srcErr = err.Error()
58 parsedErr = err
59 )
60 
61 if twirpErr, ok := err.(twirp.Error); ok {
62 srcErr = twirpErr.Msg()
63 }
64 
65 if mapped, ok := errorStrMap[srcErr]; ok {
66 parsedErr = mapped
67 }
68 
69 return parsedErr
70}
71 
72var retryableErrs []error = []error{
73 context.DeadlineExceeded,
74}
75 
76var errorStrMap map[string]error = map[string]error{}
77 
78func errorDef(str string, retryable bool) error {
79 err := &Error{
80 msg: str,
81 }
82 if retryable {
83 retryableErrs = append(retryableErrs, err)
84 }
85 errorStrMap[str] = err
86 return err
87}