I have this code:
#Saga
#Slf4j
public class ActionsSaga2 {
#Autowired
transient CommandGateway commandGateway;
#Autowired
transient ActionService actionService;
String id;
ApplicationState state;
#StartSaga
#SagaEventHandler(associationProperty = "applicationId")
public void on(ApplicationCreatedEvent event) {
id = event.getApplicationId();
state = event.getState();
commandGateway.send(ScheduleActionCommand.builder()
.applicationId(event.getApplicationId())
.actionId(id)
.targetState(event.getState())
.build());
}
#EndSaga
#SagaEventHandler(associationProperty = "applicationId")
public void on(ActionDoneEvent event) {
assert id != null;
}
}
First #SagaEventHandler on(ApplicationCreatedEvent event) sets the private fields id and state.
But in the second #SagaEventHandler on(ActionDoneEvent event) both properties are null.
I'm pretty sure that the calls are routed to the same saga (the only record in saga_entry that is deleted after the second method call)
Could you help me where is the problem?
I have no special configuration for saga, using AxonServer and Spring boot
I found the solution, the problem was in the association property.
correct event handler is
#EndSaga
#SagaEventHandler(associationProperty = "applicationId", keyName = "id")
public void on(ActionDoneEvent event) {
assert id != null;
}
Related
I have a bean, set up in a configuration class. My goal is to transform, deny, apply common tags and modify the metrics that are sent to New Relic.
Here is my configuration class
#Configuration
#Log4j2
public class MetricsConfig {
private static final Duration HISTOGRAM_EXPIRY = Duration.ofMinutes(10);
private static final Duration STEP = Duration.ofSeconds(5);
private final transient String profile;
#Autowired
public MetricsConfig(#Value("${spring.profiles.active}") final String profile) {
this.profile = profile;
}
#Bean
public MeterRegistryCustomizer<NewRelicMeterRegistry> metricsCommonTags() {
log.info("Configuring Registry");
return registry -> registry.config()
.commonTags(Arrays.asList(Tag.of("appId", "1111111"), Tag.of("environment", profile),
Tag.of("app", "aws-app-name")))
.meterFilter(new MeterFilter() {
#Override
public Meter.Id map(Meter.Id id) {
if(id.getName().startsWith("http")){
return id.withName("app-name." + profile + "." + id.getName());
}
return id;
}
#Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
return config.merge(DistributionStatisticConfig.builder()
.percentilesHistogram(true)
.percentiles(0.5, 0.75, 0.95)
.expiry(HISTOGRAM_EXPIRY)
.bufferLength((int) (HISTOGRAM_EXPIRY.toMillis() / STEP.toMillis()))
.build());
}
}).meterFilter(MeterFilter.deny(id -> {
String uri = id.getTag("uri");
log.info("id: [{}]", id);
return (uri != null && uri.startsWith("/swagger") && uri.startsWith("/manage")) || !id.getName().toLowerCase().startsWith("app-name");
}))
;
}
}
Then, I also inject MeterRegistry into some of my classes to capture custom events (Timer, Counter).
Everything works in regards to capturing the events, except that the data in New Relic is missing the commonTags, transformations, and anything else that I apply in MetricsConfig class.
Am I missing something on making sure my app is wiring up the MeterRegistryCustomizer correctly?
Arg.. I had implemented a HandlerInterceptorAdapter to attempt to implement a Counter for all requests with additional tags. Which, it did not like.
We have two tables that have a one to many relationship. When we insert multiple records into the child table across multiple threads (more specifically across multiple REST web requests) we are running into lost update issues due to a race condition.
What we need to be able to do is have JPA recognize that the entity has been updated elsewhere prior to inserting the child record. I've tried using the #Version annotation approach but that doesn't seem to do the trick as the update/insert (I guess...) is happening on another table. I tried adding a version timestamp column on the parent table that is updated on every update but that didn't seem to do the trick either.
I think what I actually need to do is get a reference to the EntityManager directly so that I can issue a lock() command on the record prior to calling save(). I'm just too new to Spring to know if
A) that is indeed the correct approach,
B) if there is a better/easier way to do what we are trying to accomplish, and
C) how to actually do that.
Also, I am aware of the #OneToMany annotation but that didn't seem to do anything.
I've truncated the code below for brevity and I also created a trimmed down version of the code that demonstrates the problem and will hopefully make it easier to see what I am trying to do. In the test if you change the thread pool number to 1 you can see the test pass.
Engagement class:
#Entity
public class Engagement implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ElementCollection(fetch = EAGER)
private List<String> assignedUsers;
#Version
private Long version;
private LocalDateTime updatedOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion(){return version;}
public void setVersion(Long version){this.version = version;}
public LocalDateTime getUpdatedOn(){
return updatedOn;
}
public void setUpdatedOn(LocalDateTime updatedOn) {
this.updatedOn = updatedOn;
}
public List<String> getAssignedUsers() {
return assignedUsers;
}
public void setAssignedUsers(List<String> assignedUsers) {
this.assignedUsers = assignedUsers;
}
public Engagement() {
}
}
User class:
public final class User {
private final String name;
private final String email;
private final String userId;
private final List<Engagement> engagements;
#ConstructorProperties({"roles", "name", "email", "userId", "engagements"})
User(String name, String email, String userId, List<Engagement> engagements) {
this.name = name;
this.email = email;
this.userId = userId;
this.engagements = engagements;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public String getName() {
return this.name;
}
public String getEmail() {
return this.email;
}
public String getUserId() {
return this.userId;
}
public List<Engagement> getEngagements() {
return this.engagements;
}
public static final class UserBuilder {
private String name;
private String email;
private String userId;
private List<Engagement> engagements;
UserBuilder() {
}
public User.UserBuilder name(String name) {
this.name = name;
return this;
}
public User.UserBuilder email(String email) {
this.email = email;
return this;
}
public User.UserBuilder userId(String userId) {
this.userId = userId;
return this;
}
public User.UserBuilder engagements(List<Engagement> engagements) {
this.engagements = engagements;
return this;
}
public User build() {
return new User(this.name, this.email, this.userId, this.engagements);
}
public String toString() {
return "User.UserBuilder(name=" + this.name + ", email=" + this.email + ", userId=" + this.userId + ", engagements=" + this.engagements + ")";
}
}
}
Thread test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class EngagementTest {
#Mock
UsersAuthService usersService;
#Autowired
EngagementsRepository engagementsRepository;
UsersAuthService authService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
authService = new UsersAuthServiceImpl(usersService, engagementsRepository);
}
#Test
public void addingMultipleUsersAtOnceSucceeds() throws InterruptedException {
Long engagementId = 1L;
String userId1 = "user1";
String userId2 = "user2";
String userId3 = "user3";
String userId4 = "user4";
String userId5 = "user5";
String auth = "asdf";
User adminUser = User.builder()
.userId("adminUser")
.email("user#user.com")
.name("Admin User")
.build();
Engagement engagement = new Engagement();
engagement.setAssignedUsers(new ArrayList<>());
engagement.getAssignedUsers().add(adminUser.getUserId());
engagementsRepository.save(engagement);
ExecutorService executorService = Executors.newFixedThreadPool(5);//change this to 1 to see the test pass
List<Callable<Engagement>> callableList = Arrays.asList(
addUserThread(engagementId, userId1, auth, adminUser),
addUserThread(engagementId, userId2, auth, adminUser),
addUserThread(engagementId, userId3, auth, adminUser),
addUserThread(engagementId, userId4, auth, adminUser),
addUserThread(engagementId, userId5, auth, adminUser));
executorService.invokeAll(callableList);
Engagement after = engagementsRepository.findById(engagementId);
assertEquals(6, after.getAssignedUsers().size());
}
private Callable<Engagement> addUserThread(Long engagementId, String userId1, String auth, User adminUser) {
return () -> authService.addUserTo(engagementId, userId1, auth, adminUser);
}
}
What's happening here is that you submit the callbacks for execution but never actually wait for their completion before checking the result. You need to use the List<Future<Engagement>> to actually wait for the results to complete before proceeding.
Something like this would do the trick:
executorService.invokeAll(callableList).forEach(it -> {
try {
it.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
});
Note that this is not a proper way to deal with the exception case but it causes the code to wait for completion. If you have that in place you see the threads properly rejecting some of the updates with an ObjectOptimisticLockingFailureException:
java.util.concurrent.ExecutionException: org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.example.racecondition.engagement.Engagement] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.example.racecondition.engagement.Engagement#1]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:206)
at com.example.racecondition.EngagementTest.lambda$0(EngagementTest.java:68)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.example.racecondition.EngagementTest.addingMultipleUsersAtOnceSucceeds(EngagementTest.java:66)
What's weird about the test case beyond that is that UsersAuthServiceImpl carries an #Transactional but the test case manually instantiates that class, so that there's no transactional proxy in place already. This causes the calls to findById(…) and save(…) from within addToUser(…) to run in two transactions. Tweaking that doesn't change the output though.
I think what I actually need to do is get a reference to the EntityManager directly so that I can issue a lock() command on the record prior to calling save(). I'm just too new to Spring to know if
A) that is indeed the correct approach,
If I understand you correctly you want to basically force a version increment on an entity so that if multiple threads do that one fails.
You can indeed achieve that by locking the entity in question using LockModeType.PESSIMISTIC_FORCE_INCREMENT or LockModeType.OPTIMISTIC_FORCE_INCREMENT.
B) if there is a better/easier way to do what we are trying to accomplish, and
C) how to actually do that.
With Spring Data probably the best way to do that is using the #Lock annotation on the method you use to load the entity.
I try to config cqrs and event sourcing with axon.
SeatReseveCreateCommand is work properly. but SeatReserveUpadateCommand is not work correct.
this is my SeatReserve aggregate
#Aggregate
public class SeatReserve {
#AggregateIdentifier
private String id;
private String seatid;
private Date date;
#SuppressWarnings("unused")
private SeatReserve() {
}
#CommandHandler
public SeatReserve(SeatReseveCreateCommand seatReseveCreateCommand) {
apply(new SeatReseveCreateEvent(seatReseveCreateCommand.getMyid(), seatReseveCreateCommand.getSeatId(),
seatReseveCreateCommand.getDate()));
}
#CommandHandler
public SeatReserve(SeatReserveUpadateCommand upadateCommand) {
apply(new SeatReserveUpadateEvent(id, upadateCommand.getSeatId()));
}
#EventSourcingHandler
public void on(SeatReseveCreateEvent seatReseveCreateEvent) {
this.id = seatReseveCreateEvent.getId();
this.seatid = seatReseveCreateEvent.getSeatId();
this.date = seatReseveCreateEvent.getDate();
}
#EventSourcingHandler
public void on(SeatReserveChangeEvent upadateEvent) {
seatid = upadateEvent.getSeatId();
}
}
this is my controller
#RestController
public class TestController {
private final CommandGateway commandGateway;
public TestController(CommandGateway commandGateway) {
this.commandGateway=commandGateway;
}
#PostMapping
public String fileComplaint(#RequestBody Map<String, String> request) {
String id = UUID.randomUUID().toString();
SeatReseveCreateCommand command=new SeatReseveCreateCommand(id,request.get("seatid"),new Date(request.get("date")));
commandGateway.send(command);
return id;
}
#PatchMapping
public String fileComplaintUpdate(#RequestBody Map<String, String> request) {
SeatReserveUpadateCommand command= new SeatReserveUpadateCommand(request.get("id"),request.get("seatid"));
commandGateway.send(command);
return request.get("id");
}
}
I try to send request using postman
this is my create request
this is my update request
update make this error
2018-01-03 10:44:53.608 WARN 11138 --- [nio-8085-exec-1] o.a.c.gateway.DefaultCommandGateway : Command 'com.thamira.research.api.bankaccount.SeatReserveUpadateCommand' resulted in org.axonframework.eventsourcing.IncompatibleAggregateException(Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.)
how can I solve this.
The problem is that your update command is defined as a constructor. The command should go to the already existing aggregate instance.
Changing the command handler to:
#CommandHandler
public void handle(SeatReserveUpadateCommand upadateCommand) {...}
should fix the issue.
I'm trying to test if #Async annotation of Spring is working as expected on my project. But It doesn't.
I have this test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = GlobalConfiguration.class)
public class ActivityMessageListenerTest {
#Autowired
private ActivityMessageListener activityMessageListener;
private Long USER_ID = 1l;
private Long COMPANY_ID = 2l;
private Date DATE = new Date(10000000);
private String CLASSNAME = "className";
private Long CLASSPK = 14l;
private Integer TYPE = 22;
private String EXTRA_DATA = "extra";
private Long RECIVED_USER_ID = 99l;
#Before
public void setup() throws Exception {
}
#Test
public void testDoReceiveWithException() throws Exception {
System.out.println("Current thread " + Thread.currentThread().getName());
Map<String, Object> values = new HashMap();
values.put(ActivityMessageListener.PARAM_USER_ID, USER_ID);
values.put(ActivityMessageListener.PARAM_COMPANY_ID, COMPANY_ID);
values.put(ActivityMessageListener.PARAM_CREATE_DATE, DATE);
values.put(ActivityMessageListener.PARAM_CLASS_NAME, CLASSNAME);
values.put(ActivityMessageListener.PARAM_CLASS_PK, CLASSPK);
values.put(ActivityMessageListener.PARAM_TYPE, TYPE);
values.put(ActivityMessageListener.PARAM_EXTRA_DATA, EXTRA_DATA );
values.put(ActivityMessageListener.PARAM_RECEIVED_USER_ID, RECIVED_USER_ID);
Message message = new Message();
message.setValues(values);
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
}
}
As you can see I'm printing the name of the current thread.
The class containing the #Async method is:
public class ActivityMessageListener extends BaseMessageListener {
public static final String PARAM_USER_ID = "userId";
public static final String PARAM_COMPANY_ID = "companyId";
public static final String PARAM_CREATE_DATE = "createDate";
public static final String PARAM_CLASS_NAME = "className";
public static final String PARAM_CLASS_PK = "classPK";
public static final String PARAM_TYPE = "type";
public static final String PARAM_EXTRA_DATA = "extraData";
public static final String PARAM_RECEIVED_USER_ID = "receiverUserId";
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
#Override
#Async(value = "activityExecutor")
public void doReceive(Message message) throws Exception {
System.out.println("Current " + Thread.currentThread().getName());
if (1> 0)
throw new RuntimeException("lalal");
Map<String, Object> parameters = message.getValues();
Long userId = (Long)parameters.get(ActivityMessageListener.PARAM_USER_ID);
Long companyId = (Long)parameters.get(ActivityMessageListener.PARAM_COMPANY_ID);
Date createDate = (Date)parameters.get(ActivityMessageListener.PARAM_CREATE_DATE);
String className = (String)parameters.get(ActivityMessageListener.PARAM_CLASS_NAME);
Long classPK = (Long)parameters.get(ActivityMessageListener.PARAM_CLASS_PK);
Integer type = (Integer)parameters.get(ActivityMessageListener.PARAM_TYPE);
String extraData = (String)parameters.get(ActivityMessageListener.PARAM_EXTRA_DATA);
Long receiverUserId = (Long)parameters.get(ActivityMessageListener.PARAM_RECEIVED_USER_ID);
ActivityLocalServiceUtil.addActivity(userId, companyId, createDate, className, classPK, type, extraData, receiverUserId);
}
}
Here I'm printing the name of the current thread inside of the #Async method, and the name is the same as before, main. So it's not working.
The GlobalConfiguration is:
#Configuration
#EnableAspectJAutoProxy
#EnableTransactionManagement
#ComponentScan({
"com.shn.configurations",
...some packages...
})
public class GlobalConfiguration {...}
And inside one of the specified packages has the activityExecutor bean:
#Configuration
#EnableAsync(proxyTargetClass = true)
public class ExecutorConfiguration {
#Bean
public ActivityMessageListener activityMessageListener() {
return new ActivityMessageListener();
}
#Bean
public TaskExecutor activityExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor =
new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
What I'm doing wrong?
Tricky.
Asynchronous behavior is added through proxying.
Spring provides you with a proxy that wraps the actual object and performs the actual invocation in a separate thread.
It looks something like this (except most of this is done dynamically with CGLIB or JDK proxies and Spring handlers)
class ProxyListener extends ActivityMessageListener {
private ActivityMessageListener real;
public ProxyListener(ActivityMessageListener real) {
this.real = real;
}
TaskExecutor executor; // injected
#Override
public void doReceive(Message message) throws Exception {
executor.submit(() -> real.doReceive(message)); // in another thread
}
}
ActivityMessageListener real = new ActivityMessageListener();
ProxyListener proxy = new ProxyListener(real);
Now, in a Spring world, you'd have a reference to the proxy object, not to the ActivityMessageListener. That is
ActivityMessageListener proxy = applicationContext.getBean(ActivityMessageListener.class);
would return a reference to the ProxyListener. Then, through polymorphism, invoking doReceive would go to the overriden Proxy#doReceive method which would invoke ActivityMessageListener#doReceive through delegation and you'd get your asynchronous behavior.
However, you're in a half Spring world.
Here
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
the reference this is actually referring to the real ActivityMessageListener, not to the proxy. So when, presumably, you send your message on the bus here
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
you're sending it to the real object, which doesn't have the proxy asynchronous behavior.
The full Spring solution would be to have the MessabeBus (and/or its queue) be Spring beans in which you can inject the fully process (proxied, autowired, initialized) beans.
In reality, since CGLIB proxies are really just subclasses of your types, so the ProxyListener above would actually also add itself to the bus since the super constructor would be invoked. It would seem though that only one MessageListener can register itself with a key, like MKTDestinationNames.ACTIVITY_REGISTRY. If this isn't the case, you'd have to show more of that code for explanation.
In your test, if you do
activityMessageListener.doReceive(message);
you should see that asynchronous behavior since activityMessageListener should hold a reference to the proxy.
I have a simple Hibernate entity:
#Entity
#Table(name = "keyword",
uniqueConstraints = #UniqueConstraint(columnNames = { "keyword" }))
public class KeywordEntity implements Serializable {
private Long id;
private String keyword;
public KeywordEntity() {
}
#Id
#GeneratedValue
#Column(unique = true, updatable=false, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name="keyword")
public String getKeyword() {
return this.keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
}
DAO for it:
#Component
#Scope("prototype")
public class KeywordDao {
protected SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public KeywordEntity findByKeyword(String keyword) throws NotFoundException {
Criteria criteria = sessionFactory.getCurrentSession()
.createCriteria(KeywordEntity.class)
.add(Restrictions.eq("keyword", keyword));
KeywordEntity entity = (KeywordEntity) criteria.uniqueResult();
if (entity == null) {
throw new NotFoundException("Not found");
}
return entity;
}
public KeywordEntity createKeyword(String keyword) {
KeywordEntity entity = new KeywordEntity(keyword);
save(entity);
return entity;
}
}
and a service, which puts everything under #Transactional:
#Repository
#Scope("prototype")
public class KeywordService {
#Autowired
private KeywordDao dao;
#Transactional(readOnly = true)
public KeywordEntity getKeyword(String keyword) throws NotFoundException {
return dao.findByKeyword(keyword);
}
#Transactional(readOnly = false)
public KeywordEntity createKeyword(String keyword) {
return dao.createKeyword(keyword);
}
#Transactional(readOnly = false)
public KeywordEntity getOrCreateKeyword(String keyword) {
try {
return getKeyword(keyword);
} catch (NotFoundException e) {
return createKeyword(keyword);
}
}
}
In a single-threaded environment this code runs just fine. The problems, when I use it in multi-threaded environment. When there are many parallel threads, working the same keywords, some of them are calling the getOrCreateKeyword with the same keyword at the same time and following scenario occurs:
2 threads at the same time call keyword service with the same keyword, both first tries to fetch the existing keyword, both are not finding, and both try to create new one. The first one succeeds, the second - causes ConstraintViolationException to be thrown.
So I did try to improve the getOrCreateKeyword method a little:
#Transactional(readOnly = false)
public KeywordEntity getOrCreateKeyword(String keyword) {
try {
return getKeyword(keyword);
} catch (NotFoundException e) {
try {
return createKeyword(keyword);
} catch (ConstraintViolationException ce) {
return getKeyword(keyword);
}
}
}
So theoretically it should solve the issues, but in practice, once ConstraintViolationException is thrown, calling the getKeyword(keyword) results in another Hibernate exception:
AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate,
but is more likely due to unsafe use of the session)org.hibernate.AssertionFailure:
null id in KeywordEntity entry (don't flush the Session after an exception occurs)
How to solve this problem?
You could use some sort of Pessimistic locking mechanism using the database/hibernate or you could make the service method getOrCreateKeyword() synchronized if you run on a single machine.
Here are some references.
Hibernates documentation http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-locking
This article shows how to put a lock on a specific entity and all entities from a result of a query which may help you.
http://www.objectdb.com/java/jpa/persistence/lock#Locking_during_Retrieval_
The solution was to discard the current session once ConstraintViolationException occurs and retrieve the keyword one more time within the new session. Hibernate Documentation also point to this:
If the Session throws an exception, the transaction must be rolled back and the session discarded. The internal state of the Session might not be consistent with the database after the exception occurs.