|
75 | 75 | //for a random retry time. Default is 10 but it is configurable.
|
76 | 76 | BaseRetryDuration = 10 * time.Millisecond
|
77 | 77 | //RPCHeaderName used to define the header in grabbit for RPC
|
78 |
| - RPCHeaderName = "x-grabbit-msg-rpc-id" |
| 78 | + RPCHeaderName = "x-grabbit-msg-rpc-id" |
| 79 | + ResurrectedHeaderName = "x-resurrected-from-death" |
| 80 | + FirstDeathRoutingKeyHeaderName = "x-first-death-routing-key" |
79 | 81 | )
|
80 | 82 |
|
81 | 83 | func (b *DefaultBus) createRPCQueue() (amqp.Queue, error) {
|
@@ -488,23 +490,70 @@ func (b *DefaultBus) sendWithTx(ctx context.Context, ambientTx *sql.Tx, toServic
|
488 | 490 |
|
489 | 491 | func (b *DefaultBus) returnDeadToQueue(ctx context.Context, ambientTx *sql.Tx, publishing *amqp.Publishing) error {
|
490 | 492 | if !b.started {
|
491 |
| - return errors.New("bus not strated or already shutdown, make sure you call bus.Start() before sending messages") |
| 493 | + return errors.New("bus not started or already shutdown, make sure you call bus.Start() before sending messages") |
| 494 | + } |
| 495 | + |
| 496 | + targetQueue, ok := publishing.Headers["x-first-death-queue"].(string) |
| 497 | + if !ok { |
| 498 | + return fmt.Errorf("bad x-first-death-queue field - %v", publishing.Headers["x-first-death-queue"]) |
| 499 | + } |
| 500 | + exchange, ok := publishing.Headers["x-first-death-exchange"].(string) |
| 501 | + if !ok { |
| 502 | + return fmt.Errorf("bad x-first-death-exchange field - %v", publishing.Headers["x-first-death-exchange"]) |
| 503 | + } |
| 504 | + routingKey, err := extractFirstDeathRoutingKey(publishing.Headers) |
| 505 | + if err != nil { |
| 506 | + return err |
492 | 507 | }
|
493 |
| - //publishing.Headers. |
494 |
| - exchange := fmt.Sprintf("%v", publishing.Headers["x-first-death-exchange"]) |
495 |
| - routingKey := fmt.Sprintf("%v", publishing.Headers["x-first-death-queue"]) |
| 508 | + |
| 509 | + publishing.Headers[FirstDeathRoutingKeyHeaderName] = routingKey // Set the original death routing key to be used later for replaying |
| 510 | + publishing.Headers[ResurrectedHeaderName] = true // mark message as resurrected |
| 511 | + // publishing.Headers["x-first-death-exchange"] is not deleted and kept as is |
496 | 512 |
|
497 | 513 | delete(publishing.Headers, "x-death")
|
498 | 514 | delete(publishing.Headers, "x-first-death-queue")
|
499 | 515 | delete(publishing.Headers, "x-first-death-reason")
|
500 |
| - delete(publishing.Headers, "x-first-death-exchange") |
| 516 | + |
| 517 | + b.Log(). |
| 518 | + WithField("message_id", publishing.MessageId). |
| 519 | + WithField("target_queue", targetQueue). |
| 520 | + WithField("first_death_routing_key", routingKey). |
| 521 | + WithField("first_death_exchange", exchange). |
| 522 | + Info("returning dead message to queue...") |
501 | 523 |
|
502 | 524 | send := func(tx *sql.Tx) error {
|
503 |
| - return b.publish(tx, exchange, routingKey, publishing) |
| 525 | + // Publishing a "resurrected" message is done directly to the target queue using the default exchange |
| 526 | + return b.publish(tx, "", targetQueue, publishing) |
504 | 527 | }
|
505 | 528 | return b.withTx(send, ambientTx)
|
506 | 529 | }
|
507 | 530 |
|
| 531 | +// Extracts the routing key of the first death of the message. "x-death" header contains a list of "deaths" that happened to this message, with |
| 532 | +// the most recent death always being first in the list, so fhe first death is the last one. More information: https://www.rabbitmq.com/dlx.html |
| 533 | +func extractFirstDeathRoutingKey(headers amqp.Table) (result string, err error) { |
| 534 | + xDeathList, ok := headers["x-death"].([]interface{}) |
| 535 | + if !ok { |
| 536 | + return "", fmt.Errorf("failed extracting routing-key from headers, bad 'x-death' field - %v", headers["x-death"]) |
| 537 | + } |
| 538 | + |
| 539 | + xDeath, ok := xDeathList[0].(amqp.Table) |
| 540 | + if !ok { |
| 541 | + return "", fmt.Errorf("failed extracting routing-key from headers, bad 'x-death' field - %v", headers["x-death"]) |
| 542 | + } |
| 543 | + |
| 544 | + routingKeys, ok := xDeath["routing-keys"].([]interface{}) |
| 545 | + if !ok { |
| 546 | + return "", fmt.Errorf("failed extracting routing-key from headers, bad 'routing-keys' field - %v", xDeath["routing-keys"]) |
| 547 | + } |
| 548 | + |
| 549 | + routingKey, ok := routingKeys[len(routingKeys)-1].(string) |
| 550 | + if !ok { |
| 551 | + return "", fmt.Errorf("failed extracting routing-key from headers, bad 'routing-keys' field - %v", xDeath["routing-keys"]) |
| 552 | + } |
| 553 | + |
| 554 | + return routingKey, nil |
| 555 | +} |
| 556 | + |
508 | 557 | //Publish implements GBus.Publish(topic, message)
|
509 | 558 | func (b *DefaultBus) Publish(ctx context.Context, exchange, topic string, message *BusMessage, policies ...MessagePolicy) error {
|
510 | 559 | return b.publishWithTx(ctx, nil, exchange, topic, message, policies...)
|
@@ -717,13 +766,3 @@ type rpcPolicy struct {
|
717 | 766 | func (p rpcPolicy) Apply(publishing *amqp.Publishing) {
|
718 | 767 | publishing.Headers[RPCHeaderName] = p.rpcID
|
719 | 768 | }
|
720 |
| - |
721 |
| -//Log returns the default logrus.FieldLogger for the bus via the Glogged helper |
722 |
| -func (b *DefaultBus) Log() logrus.FieldLogger { |
723 |
| - if b.Glogged == nil { |
724 |
| - b.Glogged = &Glogged{ |
725 |
| - log: logrus.WithField("_service", b.SvcName), |
726 |
| - } |
727 |
| - } |
728 |
| - return b.Glogged.Log() |
729 |
| -} |
|
0 commit comments