forked from eramus/worker
/
worker.go
167 lines (144 loc) · 3.45 KB
/
worker.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package worker
import (
"encoding/json"
"errors"
"strconv"
"time"
"github.com/kr/beanstalk"
)
var (
ErrBeanstalkConnect = errors.New("unable to connect to beanstalk")
ErrJSONMarshal = errors.New("json marshal")
ErrUnableToSend = errors.New("unable to send json")
ErrNoResponse = errors.New("did not receive a response")
)
// Common beanstalkd options that are used by
// this package.
type Options struct {
Host string
Count int
Reserve time.Duration
Priority uint32
Delay time.Duration
TTR time.Duration
Wait time.Duration
}
var defaultOptions = &Options{
Host: "0.0.0.0:11300",
Count: 1,
Reserve: (2 * time.Second),
Priority: 0,
Delay: time.Duration(0),
TTR: (3600 * time.Second),
Wait: (10 * time.Second),
}
// Get a copy of the default options.
func GetDefaults() Options {
return *defaultOptions
}
// A function for determining the amount of delay that should be
// used each time a job is released.
type DelayDecay func(int) time.Duration
var defaultDecay = func(retries int) time.Duration {
return time.Second
}
// The result of a job
type Result int
const (
Success Result = iota
BuryJob
DeleteJob
ReleaseJob
)
type result struct {
result Result
jobID uint64
priority uint32
delay time.Duration
}
// The function that will be performed against a unit of work.
type Func func(*Request) Response
// A container that describes the result of consuming a unit
// of work.
type Response struct {
Result Result `json:"-"`
Data interface{} `json:"data"`
Error string `json:"error"`
Delay time.Duration `json:"-"`
}
// A unit of work that is passed to a WorkerFunc
type Request struct {
id uint64 `json:"-"`
host string `json:"-"`
Data json.RawMessage `json:"data"`
Feedback bool `json:"feedback"`
}
// Helper function for retrying a job. This accepts an error and a
// number of times that a unit of work should be retried. An optional
// DelayDecay func is accepted for setting the amount of time, based
// on number of releases, that a unit of work should be delayed before
// acting against it again.
func (r *Request) RetryJob(err error, maxRetries int, delay DelayDecay) Response {
if delay == nil {
delay = defaultDecay
}
beanConn, dialErr := beanstalk.Dial("tcp", r.host)
if dialErr != nil {
// send it back as retry = 1
return Response{
Result: ReleaseJob,
Error: err.Error(),
Delay: delay(1),
}
}
defer beanConn.Close()
stats, statsErr := beanConn.StatsJob(r.id)
if statsErr != nil {
// send it back as retry = 1
return Response{
Result: ReleaseJob,
Error: err.Error(),
Delay: delay(1),
}
}
_, ok := stats["releases"]
if !ok {
// send it back as retry = 1
return Response{
Result: ReleaseJob,
Error: err.Error(),
Delay: delay(1),
}
}
releases, strErr := strconv.Atoi(stats["releases"])
if strErr != nil {
// send it back as retry = 1
return Response{
Result: ReleaseJob,
Error: err.Error(),
Delay: delay(1),
}
}
if releases >= maxRetries {
return r.BuryJob(err)
}
return Response{
Result: ReleaseJob,
Error: err.Error(),
Delay: delay(releases),
}
}
// Helper for burying a job.
func (r *Request) BuryJob(err error) Response {
return Response{
Result: BuryJob,
Error: err.Error(),
}
}
// Helper for deleting a job.
func (r *Request) DeleteJob(err error) Response {
return Response{
Result: DeleteJob,
Error: err.Error(),
}
}