func (channel *Channel) nackBelow(tag uint64, requeue bool, commitTx bool) *amqp.AMQPError { channel.ackLock.Lock() defer channel.ackLock.Unlock() // Transaction mode if channel.txMode && !commitTx { channel.txLock.Lock() defer channel.txLock.Unlock() channel.txAcks = append(channel.txAcks, amqp.NewTxAck(tag, true, requeue, true)) return nil } // Non-transaction mode var count = 0 for k, unacked := range channel.awaitingAcks { if k <= tag || tag == 0 { count += 1 // Init consumer, cFound := channel.consumers[unacked.ConsumerTag] queue, qFound := channel.server.queues[unacked.QueueName] // Initialize resource holders array var rhs = []amqp.MessageResourceHolder{channel} if cFound { rhs = append(rhs, consumer) } // requeue and release the approriate resources if requeue && qFound { // If we're requeueing we release the resources but don't remove the // reference. queue.Readd(unacked.QueueName, unacked.Msg) for _, rh := range rhs { rh.ReleaseResources(unacked.Msg) } } else { // If we aren't re-adding, remove the ref and all associated // resources err := channel.server.msgStore.RemoveRef(unacked.Msg, unacked.QueueName, rhs) if err != nil { return amqp.NewSoftError(500, err.Error(), 60, 120) } } // Remove this unacked message from the ones // we're waiting for acks on and ping the consumer // since there might be a message available now delete(channel.awaitingAcks, k) if cFound { consumer.Ping() } } } return nil }
func (channel *Channel) recover(requeue bool) { if requeue { channel.ackLock.Lock() defer channel.ackLock.Unlock() // Requeue. Make sure we update stats for _, unacked := range channel.awaitingAcks { // re-add to queue queue, qFound := channel.server.queues[unacked.QueueName] if qFound { queue.Readd(unacked.QueueName, unacked.Msg) } // else: The queue gone. The reference would have been removed // then so we don't remove it now in an else clause consumer, cFound := channel.consumers[unacked.ConsumerTag] // decr channel active channel.ReleaseResources(unacked.Msg) // decr consumer active if cFound { consumer.ReleaseResources(unacked.Msg) } } // Clear awaiting acks channel.awaitingAcks = make(map[uint64]amqp.UnackedMessage) } else { // Redeliver. Don't need to mess with stats. // We do this in a short-lived goroutine since this could end up // blocking on sending to the network inside the consumer go func() { for tag, unacked := range channel.awaitingAcks { consumer, cFound := channel.consumers[unacked.ConsumerTag] if cFound { // Consumer exists, try to deliver again channel.server.msgStore.IncrDeliveryCount(unacked.QueueName, unacked.Msg) consumer.Redeliver(tag, unacked.Msg) } else { // no consumer, drop message var rhs = []amqp.MessageResourceHolder{ channel, consumer, } channel.server.msgStore.RemoveRef(unacked.Msg, unacked.QueueName, rhs) } } }() } }