@@ -14,6 +14,11 @@ import (
14
14
// and process Messages.
15
15
type ConsumerFunc func (* Message ) error
16
16
17
+ type registeredConsumer struct {
18
+ fn ConsumerFunc
19
+ id string
20
+ }
21
+
17
22
// ConsumerOptions provide options to configure the Consumer.
18
23
type ConsumerOptions struct {
19
24
// Name sets the name of this consumer. This will be used when fetching from
@@ -62,12 +67,12 @@ type Consumer struct {
62
67
// channels.
63
68
Errors chan error
64
69
65
- options * ConsumerOptions
66
- redis * redis.Client
67
- funcs map [string ]ConsumerFunc
68
- streams []string
69
- queue chan * Message
70
- wg * sync.WaitGroup
70
+ options * ConsumerOptions
71
+ redis * redis.Client
72
+ consumers map [string ]registeredConsumer
73
+ streams []string
74
+ queue chan * Message
75
+ wg * sync.WaitGroup
71
76
72
77
stopReclaim chan struct {}
73
78
stopPoll chan struct {}
@@ -118,25 +123,43 @@ func NewConsumerWithOptions(options *ConsumerOptions) (*Consumer, error) {
118
123
return & Consumer {
119
124
Errors : make (chan error ),
120
125
121
- options : options ,
122
- redis : r ,
123
- funcs : map [string ]ConsumerFunc {} ,
124
- streams : make ([]string , 0 ),
125
- queue : make (chan * Message , options .BufferSize ),
126
- wg : & sync.WaitGroup {},
126
+ options : options ,
127
+ redis : r ,
128
+ consumers : make ( map [string ]registeredConsumer ) ,
129
+ streams : make ([]string , 0 ),
130
+ queue : make (chan * Message , options .BufferSize ),
131
+ wg : & sync.WaitGroup {},
127
132
128
133
stopReclaim : make (chan struct {}, 1 ),
129
134
stopPoll : make (chan struct {}, 1 ),
130
135
stopWorkers : make (chan struct {}, options .Concurrency ),
131
136
}, nil
132
137
}
133
138
139
+ // RegisterWithLastID is the same as Register, except that it also lets you
140
+ // specify the oldest message to receive when first creating the consumer group.
141
+ // This can be any valid message ID, "0" for all messages in the stream, or "$"
142
+ // for only new messages.
143
+ //
144
+ // If the consumer group already exists the id field is ignored, meaning you'll
145
+ // receive unprocessed messages.
146
+ func (c * Consumer ) RegisterWithLastID (stream string , id string , fn ConsumerFunc ) {
147
+ if len (id ) == 0 {
148
+ id = "0"
149
+ }
150
+
151
+ c .consumers [stream ] = registeredConsumer {
152
+ fn : fn ,
153
+ id : id ,
154
+ }
155
+ }
156
+
134
157
// Register takes in a stream name and a ConsumerFunc that will be called when a
135
158
// message comes in from that stream. Register must be called at least once
136
159
// before Run is called. If the same stream name is passed in twice, the first
137
160
// ConsumerFunc is overwritten by the second.
138
161
func (c * Consumer ) Register (stream string , fn ConsumerFunc ) {
139
- c .funcs [ stream ] = fn
162
+ c .RegisterWithLastID ( stream , "0" , fn )
140
163
}
141
164
142
165
// Run starts all of the worker goroutines and starts processing from the
@@ -146,22 +169,22 @@ func (c *Consumer) Register(stream string, fn ConsumerFunc) {
146
169
// creating the consumer group in Redis. Run will block until Shutdown is called
147
170
// and all of the in-flight messages have been processed.
148
171
func (c * Consumer ) Run () {
149
- if len (c .funcs ) == 0 {
172
+ if len (c .consumers ) == 0 {
150
173
c .Errors <- errors .New ("at least one consumer function needs to be registered" )
151
174
return
152
175
}
153
176
154
- for stream := range c .funcs {
177
+ for stream , consumer := range c .consumers {
155
178
c .streams = append (c .streams , stream )
156
- err := c .redis .XGroupCreateMkStream (stream , c .options .GroupName , "0" ).Err ()
179
+ err := c .redis .XGroupCreateMkStream (stream , c .options .GroupName , consumer . id ).Err ()
157
180
// ignoring the BUSYGROUP error makes this a noop
158
181
if err != nil && err .Error () != "BUSYGROUP Consumer Group name already exists" {
159
182
c .Errors <- errors .Wrap (err , "error creating consumer group" )
160
183
return
161
184
}
162
185
}
163
186
164
- for i := 0 ; i < len (c .funcs ); i ++ {
187
+ for i := 0 ; i < len (c .consumers ); i ++ {
165
188
c .streams = append (c .streams , ">" )
166
189
}
167
190
@@ -214,7 +237,7 @@ func (c *Consumer) reclaim() {
214
237
c .stopPoll <- struct {}{}
215
238
return
216
239
case <- ticker .C :
217
- for stream := range c .funcs {
240
+ for stream := range c .consumers {
218
241
start := "-"
219
242
end := "+"
220
243
@@ -373,7 +396,6 @@ func (c *Consumer) process(msg *Message) (err error) {
373
396
err = errors .Errorf ("ConsumerFunc panic: %v" , r )
374
397
}
375
398
}()
376
- fn := c .funcs [msg .Stream ]
377
- err = fn (msg )
399
+ err = c .consumers [msg .Stream ].fn (msg )
378
400
return
379
401
}
0 commit comments