Sending bulk mails periodically - java

I have a requirement where i have to send 100 mails/minute/user and it is time specific.(i.e All the mails i.e 100*1000 = 10000 mails/min should go on same time)
Currently there are almost 1000 users.
As well as for each email i am saving it first then sending it.
What things i need to implement for better performance and achieve goal optimally.
[Note: All the emails are sent via different accounts, so limit wont increase]
Any suggestion will be very helpful.
I am currently using Spring Boot for the project.

You can use Windows Service for this, if project run on windows server.

//Remember to set #EnableScheduling
//in the class containing your main method
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
#Component
public class ScheduledTasks {
List<Email> listOfEmails;
int nextBatch = 50;
int curBatch = 0;
//This method will run every 15 second.
#Scheduled(fixedDelay = 15000)
public void yourMethodName() {
//This will process all of your objects all at once using treads
for(int i = curBatch; i < maxBatchSize(); i++){
listOfEmails.get(i).process();
}
nextBatch+=50;
curBatch+=50;
}
private int maxBatchSize(){
if(nextBatch < listOfEmails.size()){
return nextBatch;
} else {
return listOfEmails.size();
}
}
}
public class YourObject {
Integer someTest = 0;
#Async
public void process(Email e) {
e.send();
}
}

Related

Scheduling task with parameters in Java and Springboot

I have a method doSomething(a, x) which I want to schedule x hours in advance. I have tried multiple setups but all seem to fail. Could anybody tell me a service structure or springboot feature which could accomplish this while also being testable?
This is my current setup in java and springboot which is failing because the fixedDelay is not constant as I don't want my tests to be delayed for x hours.
The desired result of this code would be:
a is printed after x hours
The service:
#Service
public class SomeService{
public void doSomething(int a, long x) {
SchedulerService scheduler = new SchedulerService(a, x, this);
scheduler.doSomethingWithA();
}
public void doSomethingWithA(int a) {
System.out.println(a);
}
}
The scheduler:
#AllArgsConstructor
public class SchedulerService {
private int a;
private final long x;
private transient SomeService someService;
#Scheduled(fixedDelay = x)
public void doSomethingWithA() {
someService.doSomethingWithA(a);
}
}
Of course the actual service is far more complex with database access etc. and the x hours is actually 10 years, but I think you get the idea.
Any help would be greatly appreciated
Spring v3.2.2 has added String parameters to the original 3 long parameters to handle this. fixedDelayString, fixedRateString and initialDelayString are now available too.
#Scheduled(fixedDelayString = "${my.fixed.delay.prop}")
public void doSomethingWithA() {
}

Hibernate session not shared between threads

I have a springboot application that implements a user referral system. One use case is that when a user signs up using a valid referral code from another user, the referrer user gets one reward point, and for every five points they get 10$ in credit. According to this, I have implemented a use case in my application that honors these business rules, and to test proper behavior under high concurrency, I've created an integration test using #DataJpaTest and spring data repositories and H2 DB as storage system. In my test, I create a first user, and then I create a certain amount of users using the first user referral code, every one of those users is created on a different thread using a thread pool executor to spawn those threads. My problem is that the users created through thread pool spawned threads don't see the first user created in the main thread, even though I'm using JpaRepository.saveAndFlush() method to save them.
Could someone give me an explanation about what's happening here? Is it because Hibernate's session is not thread-safe?
You can see my code below, the first test has been simplified to just check the amount of user's in the repository.
#DataJpaTest(includeFilters = #ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
public class JpaTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
#Qualifier("JpaUserRepository")
private JpaUserRepository userRepository;
#Autowired
#Qualifier("JpaReferralRepository")
private ReferralRepository referralRepository;
private RegisterReferredUser registerReferredUser;
private CreateUser createUser;
private GetUser getUser;
#BeforeEach
void setUp() {
registerReferredUser = new RegisterReferredUser(referralRepository, userRepository);
createUser = new CreateUser(userRepository, referralRepository, registerReferredUser);
getUser = new GetUser(userRepository);
}
#Test
void createUser_shouldWorksProperlyUnderConcurrentExecution() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
EmailAddress referrerUserEmail = EmailAddress.of("john.doe#acme.inc");
User referrerUser = createUser.execute(new CreateUserCommand(referrerUserEmail.getValue(), null));
String referralCode = referrerUser.getReferralCode().getValue();
int maxIterations = 10;
for (int i = 0; i < maxIterations; i++) {
int emailSeed = i;
executor.submit(() -> {
createUser.execute(new CreateUserCommand(anEmailAddress(emailSeed), referralCode));
});
}
executor.shutdown();
if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
fail("Executor didn't finish in time");
}
assertThat(entityManager.getEntityManager().createQuery("from JpaUser").getResultList().size()).isEqualTo(maxIterations + 1);
// This fails: just 1 user in the repository, however, if I register users without referral (without checking the existence of the first user), users are created and this pass
}
#Test
void just_a_POC() throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
userRepository.save(UserMother.aUserWithEmail("john.doe#acme.inc"));
int maxIterations = 10;
for (int i = 0; i < maxIterations; i++) {
int emailSeed = i;
executor.submit(() -> userRepository.save(UserMother.aUserWithEmail(anEmailAddress(emailSeed))));
}
executor.shutdown();
if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
fail("Executor didn't finish in time");
}
assertThat(entityManager.getEntityManager().createQuery("from JpaUser").getResultList().size()).isEqualTo(maxIterations + 1);
// This pass
}
}
In the CreateUser I have the following code:
private void assertReferralCodeIsValid(ReferralCode referralCode, EmailAddress email) {
if (!userRepository.findByReferralCode(referralCode).isPresent()) {
throw new NonExistentReferralCode(referralCode);
}
if (referralRepository.findByEmailAndCode(email, referralCode).isPresent()) {
throw new ReferralCodeAlreadyUsed(email, referralCode);
}
}
And this is the JpaUserRepository.save() method:
#Repository("JpaUserRepository")
public class JpaUserRepository implements UserRepository {
private JpaUserCrudRepository crudRepository;
public JpaUserRepository(JpaUserCrudRepository crudRepository) {
this.crudRepository = crudRepository;
}
#Override
public void save(User user) {
crudRepository.saveAndFlush(JpaUser.fromDomain(user));
}
}
Look at the isolation level configured for your transactions. Database engines usually try to serve data as fast as possible without blocking (when possible). So if all your threads read a table at the same time, they may get an "uncommited" version of the records.
If you need synchronization, you can change the isolation level, or lock the table before working on it.
More on this topic:
Spring #Transactional - isolation, propagation
https://www.baeldung.com/java-jpa-transaction-locks

How to reset metrics every X seconds?

I am trying to measure application and jvm level metrics on my application using DropWizard Metrics library.
Below is my metrics class which I am using across my code to increment/decrement the metrics. I am calling increment and decrement method of below class to increment and decrement metrics.
public class TestMetrics {
private final MetricRegistry metricRegistry = new MetricRegistry();
private static class Holder {
private static final TestMetrics INSTANCE = new TestMetrics();
}
public static TestMetrics getInstance() {
return Holder.INSTANCE;
}
private TestMetrics() {}
public void increment(final Names... metricsName) {
for (Names metricName : metricsName)
metricRegistry.counter(name(TestMetrics.class, metricName.value())).inc();
}
public void decrement(final Names... metricsName) {
for (Names metricName : metricsName)
metricRegistry.counter(name(TestMetrics.class, metricName.value())).dec();
}
public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
public enum Names {
// some more fields here
INVALID_ID("invalid-id"), MESSAGE_DROPPED("drop-message");
private final String value;
private Names(String value) {
this.value = value;
}
public String value() {
return value;
}
};
}
And here is how I am using above TestMetrics class to increment the metrics basis on the case where I need to. Below method is called by multiple threads.
public void process(GenericRecord record) {
// ... some other code here
try {
String clientId = String.valueOf(record.get("clientId"));
String procId = String.valueOf(record.get("procId"));
if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(procId)
&& !NumberUtils.isNumber(clientId)) {
TestMetrics.getInstance().increment(Names.INVALID_ID,
Names.MESSAGE_DROPPED);
return;
}
// .. other code here
} catch (Exception ex) {
TestMetrics.getInstance().increment(Names.MESSAGE_DROPPED);
}
}
Now I have another class which runs every 30 seconds only (I am using Quartz framework for that) from where I want to print out all the metrics and its count. In general, I will send these metrics every 30 seconds to some other system but for now I am printing it out here. Below is how I am doing it.
public class SendMetrics implements Job {
#Override
public void execute(final JobExecutionContext ctx) throws JobExecutionException {
MetricRegistry metricsRegistry = TestMetrics.getInstance().getMetricRegistry();
Map<String, Counter> counters = metricsRegistry.getCounters();
for (Entry<String, Counter> counter : counters.entrySet()) {
System.out.println(counter.getKey());
System.out.println(counter.getValue().getCount());
}
}
}
Now my question is: I want to reset all my metrics count every 30 seconds. Meaning when my execute method prints out the metrics, it should print out the metrics for that 30 second only (for all the metrics) instead of printing for that whole duration from when the program is running.
Is there any way that all my metrics should have count for 30 seconds only. Count of whatever has happened in last 30 seconds.
As an answer because it is too long:
You want to reset the counters. There is no API for this. The reasons are discussed in the linked github issue. The article describes a possible workaround. You have your counters and use them as usual - incrementing and decrementing. But you can't reset them. So add new Gauge which value is following the counter you want to reset after it have reported to you. The getValue() method of the Gauge is called when you want to report the counter value. After storing the current value the method is decreasing the value of the counter with it. This effectively reset the counter to 0. So you have your report and also have the counter reset. This is described in Step 1.
Step 2 adds a filter that prohibits the actual counter to be reported because you are now reporting through the gauge.

Spring Boot Progress report

I would like to be able to report a certain method's progress in Spring Boot. I used a seperate class which I store the current status in and return as the current view:
It looks like this:
public class SearchTableReloadState {
//STATIC STORAGE
public static long TABLE_ROW_COUNT = 0;
public static long CURRENT_OFFSET = 0;
public static long CURRENT_LIMIT = 0;
public static long DEFAULT_LIMIT = 20000;
public static void reset() {
TABLE_ROW_COUNT = 0;
CURRENT_OFFSET = 0;
CURRENT_LIMIT = DEFAULT_LIMIT;
}
public static void setDefaultLimit(long defaultLimit) {
DEFAULT_LIMIT = defaultLimit;
}
// VIEWMODEL
public long tableRowCount = 0;
public long currentOffset = 0;
public long currentLimit = 0;
public static SearchTableReloadState getState() {
SearchTableReloadState reloadState = new SearchTableReloadState();
reloadState.tableRowCount = TABLE_ROW_COUNT;
reloadState.currentOffset = CURRENT_OFFSET;
reloadState.currentLimit = CURRENT_LIMIT;
return reloadState;
}
}
And the methods:
#RequestMapping(value = {"/manage/searchtable/reload/state"}, method = RequestMethod.GET)
public #ResponseBody SearchTableReloadState searchTableReloadState() {
return SearchTableReloadState.getState();
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = {"/manage/searchtable/reload"}, method = RequestMethod.GET)
public void searchTableReload() throws ResourceAlreadyExistsException, ParameterMissingIdException {
SearchTableReloadState.reset();
SearchTableReloadState.TABLE_ROW_COUNT = productDataReferenceDao.countJobReferences();
productDataReferenceDao.truncateSearchTable();
while (SearchTableReloadState.CURRENT_OFFSET < SearchTableReloadState.TABLE_ROW_COUNT) {
... long running task
....
SearchTableReloadState.CURRENT_OFFSET += SearchTableReloadState.CURRENT_LIMIT;
}
}
The method with the /state would report the current state, so I could call these with Ajax on a site. Problem is, If I start the long running one, the state report request won't complete until the long running did not complete. I thought Spring uses separate threads for each request. Do I need to implement threading in Spring?
If I use the #Async annotation for the long running process, it works like I expected, but I still don't understand, why could two separate HTTP requests for a different method block each other!
If I use the #Async annotation on the method that is supposed to take a long time, the HTTP Request calling it will get a response immediately and it will run in the background and I can call the state method as I expected. Even though it is working, I still don't know why it won't work without the asynchronous execution.
If you want to use the #Async annotation, you have to put the #EnableAsync annotation on the class you used the #SpringBootApplication and/or #EnableAutoConfiguration.
I hope someone can provide a better answer later.

Expose current progress of an #Asynchronous function to use in View

In my JEE6-App (running on Glassfish 3.0.1) I have an EmailEJB which has to send lots of mails. The mails are sent asynchronously, so its annotated with the new EJB3.1 #Asynchronous, letting it be run in a separate Thread. Now i want the user to be informed about the current status of the method: How many mails have already been sent?
Sending the mails asynchronously works fine, but i can't figure out how to let the progress be accessible from outside. Seems like my approach to do that is quite wrong, but somehow it has to be possible (maybe another approach). This is how my EmailEJB currently looks like (its kind of pseudo code, but explains what i want):
#Stateful
public class EmailEJB {
#Asynchronous
public Future<Integer> sendMails() {
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
// i want to return the progress without returning ;)
return new AsyncResult<Integer>(i)
}
}
}
//Just for the completeness... from outside, i'm accessing the progress like this:
Future<Integer> progress = emailEJB.sendEmails();
Integer currentvalue = progress.get();
How can i return the current progress inside my asynchronous function, without cancelling it with a return? How can i show the user the progress of a loop inside a function? Do i need another asynchronous method? Any hints?
Nobody? Ok so this is my solution. Im not sure if this is a big fat workaround or just a way to get this done.
Since an #Asynchronous method cannot access the Session context, and therefore also no Session Beans (at least i dont know how, i always got ConcurrentModificationErrors or similar ones) i created a Singleton ProgressEJB, which contains a HashMap:
#Singleton #LocalBean #Startup
public class ProgressEJB {
private HashMap<String, Integer> progressMap = new HashMap<String, Integer>
// getters and setters
}
This hashmap should map the SessionId (a String) to an Integer value (the progress 0->100). So a user session is associated with a progress.
In my EmailEJB, i'm injecting this ProgressEJB, and in my #Asynchronous method, i'm increasing the value everytime an email has been sent:
#Stateful #LocalBean
public class EmailEJB {
#Inject
private ProgressEJB progress;
// Mail-Settings
...
#Asynchronous
public void sendEmails(user:User, message:Message, sessionId:String) {
progress.progressMap.put(sessionId, 0);
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
progress.getProgressMap().put(sessionId, (i / mails.size) * 100)
}
progress.getProgressMap().remove(sessionId);
}
The sessionId comes from my Managed (Weld) Bean, when calling the function:
#SessionScoped
#Named
public class EmailManager {
#Inject
private ProgressEJB progress;
#Inject
private FacesContext facesContext;
private String sessionId;
#PostConstruct
private void setSessionId() {
this.sessionId = ((HttpSession)facesContext.getExternalContext().getSession(false)).getId();
}
public Integer getProgress() {
if (progress.getProgressMap().get(sessionId) == null)
return 100;
else
return progress.getProgressMap().get(sessionId);
}
}
Now i can access progress from EmailManager from my JSF view with Ajax Polling, telling the user how many mails already have been sent. Just tested it with 2 users, seems to work.
I also see only a #Singleton solution here.
But this imply the need of Housekeeping in ProgressEJB. E.g. some effort is needed to prune old session from Hashmap.
Another solution is described in
Is there any way to know the progress of a EJB Asynchronous process?
This solution does not need a Stateful Bean.
#Stateless
public class EmailEJB {
// Mail-Settings
...
#Asynchronous
public void sendEmails(User user, Message message, WorkContext context) {
progress.progressMap.put(sessionId, 0);
for (int i=0; i<mails.size; i++) {
sendMail(mails[i])
context.setProgress((i / mails.size) * 100)
}
context.setRunning(false);
}
}
The Context-Object, which holds the progress.
public class WorkContext {
//volatile is important!
private volatile Integer progress = 0;
private volatile boolean running = false;
// getters & setters
}
The usage is very easy.
#SessionScoped
#Named
public class EmailManager {
#Inject
private EmailEJB emailEJB;
private WorkContext workContext;
public void doStuff() {
workContext = new WorkContext();
emailEJB.sendEmails(user, message, workContext)
}
public Integer getProgress() {
return workContext.getProgress();
}
....
}

Categories