-
Notifications
You must be signed in to change notification settings - Fork 2
FAQ & Debugging
A: Make sure of the following points:
- You have registered the handler via
Builder.RegisterHandler
. If you do not do this, Messenger will not know about your handler, and you will receive a warning“no handlers registered for message type”
in the logs or an error duringDispatch
. - Check that the type of message you are dispatching exactly matches the type expected by the handler. For example, if a handler is defined for
*MyMessage
, and you send the valueMyMessage (not a pointer)
, then Messenger will see a different type. It is usually better to send a pointer to a structure if the handler is declared with a pointer (as in the examples above). - if the message is sent via an external transport (for example, RabbitMQ), make sure that you have called
messenger.Run(ctx)
– without starting, consumers will not be activated and messages in the queue will not be processed. If Messenger is running in multi-bus mode, check that your handler is either ondefault_bus
or implementsGetBusName()
correctly, and that the message is being routed to the correct bus. - check the logs (logger): the built-in logger slog at the
Debug
level can give hints what is happening with the message (for example, it was sent to an external transport and therefore was not processed locally, or handlers were not found).
A: In the YAML configuration, in the routing section, you need to use the name of the message type. Messenger is trying to match this string with the registered handler types. If your type is not in the main package and several packages may have structures of the same name, you must specify the full name (for example, pkg.MessageType). Mapping errors result in the message “failed to resolve message type 'X' in routing configuration”
when calling `Build()'. If you receive such an error, you can:
- check the spelling/namespace of the type in the config,
- make sure that the appropriate handler is registered before calling
Build()
, - as a last resort, explicitly register the message via
RegisterMessage(&MyMessage{})
(this will fill theTypeResolver
with type information, even without a handler). The latter method is useful for services that only send messages, but do not process them, so that routing does not crash on an unknown type.
A: The Run(ctx)
method will return an error when ctx
is canceled (for example, if you passed context.Background()
without timeout and immediately canceled it, or if ctx
was tied to the end of the program). Usually, Run
is run in a separate goroutine and context' is used.Background()
or another long-lived context. Make sure that the 'ctx` is not canceled ahead of time. For example, don't do this:
ctx, cancel := context.WithCancel(context.Background())
cancel()
messenger.Run(ctx) // will return immediately.Err()
In this case, Run
will exit almost instantly. The correct way is to call Run
and then cancel the context when the application is terminated (for example, when an OS signal is received).
A: If different services (processes) need to exchange messages through a broker, make sure that:
- they are configured on the same broker (DSN, exchange, queues, binding keys). Check that exchange.name and the binding keys match.
- the
auto_setup
option is enabled in the amqp transport configuration (or you created the exchange/queue yourself in advance). If `auto_setup: false', you need to manually declare the exchange and queues in RabbitMQ, otherwise nothing will bind. - check that the exchange type is correct (for example, topic) and the binding key corresponds to the routing key of the messages being sent.
- make sure that the consumer service has actually started
Run
and is listening to the queue (when starting Messenger withauto_setup
andconsumer_pool_size > 0
, you should see that consumers are connected; you can check in RabbitMQ Management that there are active consumers).
A: The retry mechanism in Messenger works at the transport level. The built-in amqp
transport implements the RetryableTransport
interface (the Retry
method). When sending a message to RabbitMQ fails (for example, the broker is unavailable), Messenger generates the SendFailedMessageEvent
event. A listener is registered in the builder, which intercepts this event and decides whether to retry sending. This standard listener, the SendFailedMessageForRetryListener
, uses the MultiplierRetryStrategy
: it counts down the attempts and the delays between them. It calls transport.Retry(ctx, env)
to resend. Repeats continue until max_retries
is exceeded or until the message is successfully sent. If sending still fails after the maximum number of attempts, the listener will check if FailureTransport
is specified. If yes, the message will be sent to this backup transport (usually a configured "dead-letter" queue). If failure_transport
is not set, the message will simply remain unsent; you will see a sending error in the logs on the last attempt. To handle such situations: you can set up a failure_transport
in the config (and a separate consumer for this "drop-off" queue for manual processing or alerts), or add your listener to the SendFailedMessageEvent
, which, for example, will log or notify developers about the problem.
A: Yes, it is convenient to increase the logging level for this. Messenger uses log/slog
. By default, slog.Default()
is the info level. You can configure your slog.Logger
with Level=Debug
and pass it to newBuilder
. Messenger already contains some debug logs: for example, EventDispatcher
logs adding a listener and dispatching an event (to debug), SendMessageMiddleware
logs when it does not find transports or sends a message, HandleMessageMiddleware
logs the absence of handlers and successful message processing.
These messages at the Debug level will help you understand whether the message is passing through the chain, which transports are being selected, and whether listeners are being called.
In addition, you can always add your own middleware or debugging listeners. For example, your LoggerMiddleware
(from the example above) will output each stage to the console. Or the listener on SendMessageToTransportsEvent
will show that the message is going to be sent and where. Such tools allow you to enlighten the "insides" of Messenger during execution.
A: Messenger imposes strict requirements on the signatures of the Handle
methods. For Handlers: the method must accept the context and message, and return either error
or (something, error)
. If, for example, you accidentally wrote a method without a context.Context
or pointers did not match, RegisterHandler will return an error like “Handle method must accept exactly 2 parameters”
or “last return value must be error”
. Correct the signature according to the requirements (see Quick Start). For Listeners
: make sure that your function/method accepts 1 or 2 arguments (Context is optional, event is required). For example, if you wrote the Handle()
method without parameters or with the wrong parameter type, Dispatcher
will not call it. The logs may contain the message “listener does not have Handle method”
. Check the listener definition more carefully.
A: To stop correctly, you need to cancel the context passed to messenger.Run(ctx)
. When ctx.Done()
closes, the Run
method initiates a stop: it stops all transports (Manager stops their receive cycles) and exits with ctx.Err() == context.Cancelled
or another reason for closing. In your code, if you run Run
in goroutine, you can, for example, intercept the signal OS (SIGINT/SIGTERM)
, call cancel()
at the context, then wait for Run
to finish (for example, via WaitGroup
or simple pause
). Messenger does not store unsent messages at the exit (except for those that are already in transport). If additional actions are needed upon completion (for example, to wait for all current message processing to complete), you may need to monitor the status yourself (Messenger immediately stops reading new messages, but current processing may still be performed). As a rule, it is enough to call cancel
and give the application a few seconds to terminate.