New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SagaCreationException causes are obfuscated #730
Comments
@mattupstate digging through Axon code does not immediately provide me a clear answer why your exception stack trace ends with the Would you be in the liberty to provide a snippet of your Saga, specifically with the For you final quesiton: The Whilst we're on the topic of scheduling events, you might be interested in the Concluding though, if you would be able to provide some sample code of your saga, I think I will be able to write a dedicated test to verify this scenario and potentially adding clearer logging to the creation process. |
@smcvb I realize now that a default At any rate, seeing as this is just a toy app so far, here is what my saga implementation looks like with the caveat that it looks like this today, though it should look more or less the same as it did when I was experiencing the issue: package com.example.domain.aggregate;
import com.example.domain.command.CompleteRegistrationCommand;
import com.example.domain.command.StartEmailAddressVerificationCommand;
import com.example.domain.event.EmailAddressVerificationCompletedEvent;
import com.example.domain.event.PhoneNumberVerificationCompletedEvent;
import com.example.domain.event.RegistrationCompletedEvent;
import com.example.domain.event.RegistrationDeadlineExpiredEvent;
import com.example.domain.event.RegistrationStartedEvent;
import com.example.domain.vo.UserId;
import com.example.domain.vo.VerificationToken;
import java.io.Serializable;
import java.time.Duration;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.eventhandling.saga.EndSaga;
import org.axonframework.eventhandling.saga.SagaEventHandler;
import org.axonframework.eventhandling.saga.StartSaga;
import org.axonframework.eventhandling.scheduling.EventScheduler;
import org.axonframework.eventhandling.scheduling.ScheduleToken;
import org.axonframework.spring.stereotype.Saga;
import org.springframework.beans.factory.annotation.Autowired;
@Slf4j
@Saga
public class UserRegistrationSaga implements Serializable {
@Setter(onMethod_ = {@Autowired})
transient CommandGateway commandGateway;
@Setter(onMethod_ = {@Autowired})
transient EventScheduler eventScheduler;
boolean emailAddressVerified = false;
boolean phoneNumberVerified = false;
ScheduleToken deadlineScheduleToken;
@StartSaga
@SagaEventHandler(associationProperty = "userId")
public void handle(final RegistrationStartedEvent event) {
log.debug("Received {}", event);
final UserId userId = event.getUserId();
final Duration completeRegistrationWithin = event.getCompleteRegistrationWithin();
final String emailAddress = event.getEmailAddress();
final String postEmailAddressVerificationUrl =
event.getPostEmailAddressVerificationRedirectUrl();
final Duration verifyEmailAddressWithin = event.getVerifyEmailAddressWithin();
deadlineScheduleToken =
eventScheduler.schedule(
completeRegistrationWithin, new RegistrationDeadlineExpiredEvent(userId));
log.debug(
"Scheduled RegistrationDeadlineExpiredEvent to be published {} from now",
completeRegistrationWithin);
commandGateway.send(
new StartEmailAddressVerificationCommand(
userId,
emailAddress,
postEmailAddressVerificationUrl,
verifyEmailAddressWithin,
new VerificationToken()));
}
@SagaEventHandler(associationProperty = "userId")
public void handle(final EmailAddressVerificationCompletedEvent event) {
log.debug("Received {}", event);
emailAddressVerified = true;
checkForCompleteness(event.getUserId());
}
@SagaEventHandler(associationProperty = "userId")
public void handle(final PhoneNumberVerificationCompletedEvent event) {
log.debug("Received {}", event);
phoneNumberVerified = true;
checkForCompleteness(event.getUserId());
}
@EndSaga
@SagaEventHandler(associationProperty = "userId")
public void handle(final RegistrationCompletedEvent event) {
log.debug("Received {}", event);
eventScheduler.cancelSchedule(deadlineScheduleToken);
logSagaHasEnded(event.getClass(), event.getUserId());
}
@EndSaga
@SagaEventHandler(associationProperty = "userId")
public void handle(final RegistrationDeadlineExpiredEvent event) {
log.debug("Received {}", event);
logSagaHasEnded(event.getClass(), event.getUserId());
}
private void checkForCompleteness(final UserId userId) {
if (emailAddressVerified && phoneNumberVerified) {
commandGateway.send(new CompleteRegistrationCommand(userId));
}
}
private void logSagaHasEnded(final Class eventClass, final UserId userId) {
logSagaHasEnded(eventClass.getSimpleName(), userId);
}
private void logSagaHasEnded(final String eventName, final UserId userId) {
log.debug("{} caused UserRegistrationSaga to end. {}", eventName, userId);
}
} |
Are you using Spring to auto configure your components @mattupstate? If you're not, you will can leverage the |
@smcvb yes, I'm using Axon in a Spring Boot application. Everything works as expected with Lombok or with plain old setters with package com.example.web.config;
import org.axonframework.eventsourcing.eventstore.EventStorageEngine;
import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine;
import org.axonframework.spring.eventhandling.scheduling.java.SimpleEventSchedulerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("dev")
public class DevAxonConfiguration {
@Bean
public EventStorageEngine eventStorageEngine() {
return new InMemoryEventStorageEngine();
}
@Bean
public SimpleEventSchedulerFactoryBean simpleEventSchedulerFactoryBean() {
return new SimpleEventSchedulerFactoryBean();
}
@Bean
public CommandBusPostProcessor commandBusPostProcessor() {
// Add's command message interceptors
return new CommandBusPostProcessor();
}
} Now, if I remove
So the issue here, I think, is that Axon does not yet surface the underlying exception so I can understand exactly what went wrong when Axon tried to create an instance of the saga. But perhaps I'm missing something? |
Thanks for the explanation @mattupstate. I get the feeling though that the issue you had, that the That might make the issue less pressing for now, albeit still a nice clean up thing. |
Yes, the issue I had had been solved. Just might have been able to solve it a bit quicker with some help from Axon ;-) |
Completely correct @mattupstate! I can assure you we will work on this so that future users will not run into the same issues you ran in to. |
The reason the exception isn't logged in its full "stacktrace glory" is because the result is also returned as part of the So I don't think the framework itself should do anything more than it's currently doing. |
While developing a toy application I encountered a problem with saga creation. The exact problem was difficult to determine because of the exception handling hierarchy and minimal log output from the framework. For instance, the only feedback from Axon is from a log message produced by
DefaultCommandGateway
that looks like:After digging around the source code and not finding anything that would log the underlying cause, I went ahead and installed a patch in my local Maven repository to afford myself more verbose log output (because I'm a bad debugger). I added a logger and some output in the
AnnotatedSagaRepository::doCreateInstance
method since that appears to be closest to the exception. Turns out, I didn't have anEventScheduler
bean in my Spring Application context, of which the saga had a dependency on.Maybe this is by design, or I'm missing something altogether, but I would expect some way to learn about the underlying cause of a saga not being able to be created via some log output from Axon.
Finally, and this is unrelated to the aforementioned issue, but is it also by design that the
axon-spring-boot-autoconfigure
module doesn't setup aSimpleEventScheduler
bean by default?The text was updated successfully, but these errors were encountered: