func (channel *Channel) commitTx() *amqp.AMQPError { channel.txLock.Lock() defer channel.txLock.Unlock() // messages queueMessagesByQueue, err := channel.server.msgStore.AddTxMessages(channel.txMessages) if err != nil { return amqp.NewSoftError(500, err.Error(), 60, 40) } for queueName, qms := range queueMessagesByQueue { queue, found := channel.server.queues[queueName] // the if !found { continue } for _, qm := range qms { if !queue.Add(qm) { // If we couldn't add it means the queue is closed and we should // remove the ref from the message store. The queue being closed means // it is going away, so worst case if the server dies we have to process // and discard the message on boot. var rhs = []amqp.MessageResourceHolder{channel} channel.server.msgStore.RemoveRef(qm, queueName, rhs) } } } // Acks // todo: remove acked messages from persistent storage in a single // transaction for _, ack := range channel.txAcks { channel.txAckMessage(ack) } // Clear transaction channel.txMessages = make([]*amqp.TxMessage, 0) channel.txAcks = make([]*amqp.TxAck, 0) return nil }
func (server *Server) publish(exchange *exchange.Exchange, msg *amqp.Message) (*amqp.BasicReturn, *amqp.AMQPError) { // Concurrency note: Since there is no lock we can, technically, have messages // published after the exchange has been closed. These couldn't be on the same // channel as the close is happening on, so that seems justifiable. if exchange.Closed { if msg.Method.Mandatory || msg.Method.Immediate { var rm = server.returnMessage(msg, 313, "Exchange closed, cannot route to queues or consumers") return rm, nil } return nil, nil } queues, amqpErr := exchange.QueuesForPublish(msg) if amqpErr != nil { return nil, amqpErr } if len(queues) == 0 { // If we got here the message was unroutable. if msg.Method.Mandatory || msg.Method.Immediate { var rm = server.returnMessage(msg, 313, "No queues available") return rm, nil } } var queueNames = make([]string, 0, len(queues)) for k, _ := range queues { queueNames = append(queueNames, k) } // Immediate messages if msg.Method.Immediate { var consumed = false // Add message to message store queueMessagesByQueue, err := server.msgStore.AddMessage(msg, queueNames) if err != nil { return nil, amqp.NewSoftError(500, err.Error(), 60, 40) } // Try to immediately consumed it for queueName, _ := range queues { qms := queueMessagesByQueue[queueName] for _, qm := range qms { queue, found := server.queues[queueName] if !found { // The queue must have been deleted since the queuesForPublish call continue } var oneConsumed = queue.ConsumeImmediate(qm) var rhs = make([]amqp.MessageResourceHolder, 0) if !oneConsumed { server.msgStore.RemoveRef(qm, queueName, rhs) } consumed = oneConsumed || consumed } } if !consumed { var rm = server.returnMessage(msg, 313, "No consumers available for immediate message") return rm, nil } return nil, nil } // Add the message to the message store along with the queues we're about to add it to queueMessagesByQueue, err := server.msgStore.AddMessage(msg, queueNames) if err != nil { return nil, amqp.NewSoftError(500, err.Error(), 60, 40) } for queueName, _ := range queues { qms := queueMessagesByQueue[queueName] for _, qm := range qms { queue, found := server.queues[queueName] if !found || !queue.Add(qm) { // If we couldn't add it means the queue is closed and we should // remove the ref from the message store. The queue being closed means // it is going away, so worst case if the server dies we have to process // and discard the message on boot. var rhs = make([]amqp.MessageResourceHolder, 0) server.msgStore.RemoveRef(qm, queueName, rhs) } } } return nil, nil }