@@ -67,21 +67,33 @@ func (dc *DeliveryContext) RequeueWithAnnotations(ctx context.Context, annotatio
6767type Consumer struct {
6868 receiver atomic.Pointer [amqp.Receiver ]
6969 connection * AmqpConnection
70- linkName string
70+ options ConsumerOptions
7171 destinationAdd string
7272 id string
73+
74+ /*
75+ currentOffset is the current offset of the consumer. It is valid only for the stream consumers.
76+ it is used to keep track of the last message that was consumed by the consumer.
77+ so in case of restart the consumer can start from the last message that was consumed.
78+ For the AMQP queues it is just ignored.
79+ */
80+ currentOffset int64
7381}
7482
7583func (c * Consumer ) Id () string {
7684 return c .id
7785}
7886
79- func newConsumer (ctx context.Context , connection * AmqpConnection , destinationAdd string , linkName string , args ... string ) (* Consumer , error ) {
87+ func newConsumer (ctx context.Context , connection * AmqpConnection , destinationAdd string , options ConsumerOptions , args ... string ) (* Consumer , error ) {
8088 id := fmt .Sprintf ("consumer-%s" , uuid .New ().String ())
8189 if len (args ) > 0 {
8290 id = args [0 ]
8391 }
84- r := & Consumer {connection : connection , linkName : linkName , destinationAdd : destinationAdd , id : id }
92+
93+ r := & Consumer {connection : connection , options : options ,
94+ destinationAdd : destinationAdd ,
95+ currentOffset : - 1 ,
96+ id : id }
8597 connection .entitiesTracker .storeOrReplaceConsumer (r )
8698 err := r .createReceiver (ctx )
8799 if err != nil {
@@ -91,7 +103,24 @@ func newConsumer(ctx context.Context, connection *AmqpConnection, destinationAdd
91103}
92104
93105func (c * Consumer ) createReceiver (ctx context.Context ) error {
94- receiver , err := c .connection .session .NewReceiver (ctx , c .destinationAdd , createReceiverLinkOptions (c .destinationAdd , c .linkName , AtLeastOnce ))
106+ if c .currentOffset >= 0 {
107+ // here it means that the consumer is a stream consumer and there is a restart.
108+ // so we need to set the offset to the last consumed message in order to restart from there.
109+ // In there is not a restart this code won't be executed.
110+ if c .options != nil {
111+ // here we assume it is a stream. So we recreate the options with the offset.
112+ c .options = & StreamConsumerOptions {
113+ ReceiverLinkName : c .options .linkName (),
114+ InitialCredits : c .options .initialCredits (),
115+ // we increment the offset by one to start from the next message.
116+ // because the current was already consumed.
117+ Offset : & OffsetValue {Offset : uint64 (c .currentOffset + 1 )},
118+ }
119+ }
120+ }
121+
122+ receiver , err := c .connection .session .NewReceiver (ctx , c .destinationAdd ,
123+ createReceiverLinkOptions (c .destinationAdd , c .options , AtLeastOnce ))
95124 if err != nil {
96125 return err
97126 }
@@ -105,6 +134,12 @@ func (c *Consumer) Receive(ctx context.Context) (*DeliveryContext, error) {
105134 if err != nil {
106135 return nil , err
107136 }
137+
138+ if msg != nil && msg .Annotations != nil && msg .Annotations ["x-stream-offset" ] != nil {
139+ // keep track of the current offset of the consumer
140+ c .currentOffset = msg .Annotations ["x-stream-offset" ].(int64 )
141+ }
142+
108143 return & DeliveryContext {receiver : c .receiver .Load (), message : msg }, nil
109144}
110145
0 commit comments