I want add and delete schedule tasks in runtime from views, is this possible? Maybe someone has an example code or good aticle about it?
Consider this approach. Instead of adding and deletion scheduled tasks, you may check every minute (or with another precision) actual moment against your views and run necessary tasks immediately. This will be easier. Check Quartz Scheduler, its CronExpression has isSatisfiedBy(Date date) method.
#Scheduled(cron = "5 * * * * *) // do not set seconds to zero, cause it may fit xx:yy:59
public void runTasks() {
LocalTime now = LocalTime.now(); // or Date now = new Date();
// check and run
}
I have met the same problem with you. Maybe, I can provide a not such good solution with the help of redis or database.
In the scheduled task, you can read a flag from redis, then you can decide if continue the task. For example
#Scheduled(cron = "....")
void myTask() {
Boolean flag = readFlagFromRedis(); // you can write the flag into redis or database to control the task
if (flag) {
// continue your task
}
}
then, you can control the tasks schedule in runtime.
Although I don't think this is a beautiful solution, it can meet your requirements
Related
I've got a SpringBoot Scheduler that looks like this:
#Scheduled(cron = "*/10 * * * * * ") // Every 10 seconds
public void scheduleTaskUsingCronExpression() {
long now = System.currentTimeMillis() / 1000;
System.out.println("schedule tasks using cron jobs - " + now);
}
I'd like to get the time of the next event, so I can print it out at the end of each event. I've read about using CronExpression, which has a next() method, but not sure how I get the CronExpression. OR do I create a CronExpression and somehow pass it in instead of using the #Scheduled annotation?
I'm not sure I can get accurate interval time but I can get roughly correct next running time from following code.
#Value("${cron.schedule}") String schedule;
#Scheduled(cron = "${cron.schedule}") // Every 10 seconds
public void scheduleTaskUsingCronExpression() {
var expression = CronExpression.parse(schedule);
var result = expression.next(LocalDateTime.now());
System.out.println("schedule tasks using cron jobs - " + result);
}
You can define your cron string (that you use in the #Scheduled annotation) as a final field of your class, and then create CronExpression from it to find out the next trigger date.
Also, instead of using #Scheduled annotation, you can take a look at Spring's TaskScheduler (from the package org.springframework.scheduling). It has the method TaskScheduler#schedule with two arguments: a Runnable that will run in background and a CronTrigger to set the cron expression and and the timezone of executing background tasks.
UPD. One other way to reuse your cron is set it in your application.properties and use within #Value and #Scheduled annotations, e.g. #Scheduled(cron = ${property.from.file}). In this case you can also change the cron expression before running your application if needed.
I am developing a web application with Vapor 4. It would be useful to persist client-made data on the server side for a few minutes at a time in between requests. I want to use sessions to do this. However, I am a bit confused on how the best way to automatically destroy this data after a set time. Should I make a job and have it check periodically? Or is there an easy way to set an expiry time on session creation?
I have used a bit of Middleware to achieve this for some months and it is very reliable.
It compares the timestamp now to the value from the immediate previous request. If the difference is greater than the allowed session timeout, it forces a logout.
I had to give a bit of thought to initialising the timestamp and "BAD" ensures a nil gets returned from trying to initialise a Double, which then gets the current timestamp to start the session 'timer'. I think this is safe as the user can't log in without having made at least one route call beforehand and I have other Middleware that checks to make sure the user is logged in. Try this:
struct SessionTimeoutMiddleware:Middleware
{
func respond(to request:Request, chainingTo next:Responder) -> EventLoopFuture<Response>
{
let lastRequestTimeStamp = Double(request.session.data["lastRequest"] ?? "BAD") ?? Date().timeIntervalSince1970
request.session.data["lastRequest"] = String(Date().timeIntervalSince1970)
if Date().timeIntervalSince1970 - lastRequestTimeStamp > 300.0 // seconds
{
request.auth.logout(User.self)
return request.eventLoop.makeSucceededFuture(request.redirect(to:"/somewhere/safe"))
}
return next.respond(to:request)
}
}
Then, register in configure.swift using:
let userAuthSessionsMW = User.authenticator()
let sessionTimeoutMW = SessionTimeoutMiddleware()
let timed = app.grouped(C.URI.Users).grouped(userAuthSessionsMW, sessionTimeoutMW)
try SecureRoutes(timed)
We have this fancy monitoring system to which our spring-boot services are posting metrics to an influx DB with micrometer. There's a nice grafana frontend, but the problem is that we're now at a stage where we have to have some of these metrics available in other services to reason on.
The whole system was set up by my predecessor, and my current understanding of it is practically zero. I can add and post new metrics, but I can't for the life of me get anything out of it.
Here's a short example:
Our gateway increments the counter for each image that a camera posts to it. The definition of the counter looks like this:
private val imageCounters = mutableMapOf<String, Counter>()
private val imageCounter = { camera: String ->
imageCounters.getOrPut(camera) {
registry.counter("gateway.image.counter", "camera", camera)
}
And the counter is incremented in the code like this:
imageCounter("placeholder-id").increment()
Now we're improving our billing, and the billing service needs to know how many images for a certain camera went through the gateway. So naturally the first thing I try looks like this:
class MonitoringService(val metrics: MeterRegistry) {
private val log = logger()
private val imageCounters = mutableMapOf<String, Counter>()
private val imageCounter = { camera: String ->
imageCounters.getOrPut(camera) {
metrics.counter("gateway.image.counter", "camera", camera)
}
}
fun test() {
val test = imageCounter("16004").count()
val bugme = true
log.info("influx test: $test")
}
}
There's two problems with this: First off it always returns zero, so obviously I'm doing it wrong. I just can't figure out what it is.
Second, even if it would return a reasonable value, I don't see a way to limit this by time (I'll usually need the number of images uploaded during the current month).
What worries me is that while I can find a lot of documentation on how to post data with micrometer, I can't seem to find any documentation on how to query. Is Micrometer only designed to post monitoring data, but not query it? the .getOrPut() method would indicate it can do both, but since querying data seems undocumented as far as I can tell, that might be a misconception on my part.
There is an influx-db client for Java, which I'll try next, but at the end of the day I don't want multiple components in my application doing the same thing just because I'm not familiar with the tools I inherited.
InfluxMeterRegistry is a StepMeterRegistry, so the created Counter from it is a StepCounter. StepCounter.increment() increments the count in the current step but StepCounter.count() will return the count in the previous step. That's why you're seeing 0 with count() although you've already invoked increment() several times. You can see it in the next step and the default step is 1 minute, so you have to wait for 1 minute to see it.
See the following test to get an idea on how it works: https://github.com/izeye/sample-micrometer-spring-boot/blob/influx/src/test/java/com/izeye/sample/InfluxMeterRegistryTests.java
In Spring Batch, how to loop the reader,processor and writer for N times?
My requirement is:
I have "N" no of. customers/clients.
For each customer/client, I need to fetch the records from database (Reader), then I have to process (Processor) all records for the customer/client and then I have to write the records into a file (Writer).
How to loop the spring batch job for N times?
AFAIK I'm afraid there's no framework support for this scenario. Not at least the way you want to solve it.
I'd suggest to solve the problem differently:
Option 1
Read/Process/Write all records from all customers at once.You can only do this if they are all in the same DB. I would not recommend it otherwise, because you'll have to configure JTA/XA transactions and it's not worth the trouble.
Option 2
Run your job once for each client (best option in my opinion). Save necessary info of each client in different properties files (db data connections, values to filter records by client, whatever other data you may need specific to a client) and pass through a param to the job with the client it has to use. This way you can control which client is processed and when using bash files and/or cron. If you use Spring Boot + Spring Batch you can store the client configuration in profiles (application-clientX.properties) and run the process like:
$> java -Dspring.profiles.active="clientX" \
-jar "yourBatch-1.0.0-SNAPSHOT.jar" \
-next
Bonus - Option 3
If none of the abobe fits your needs or you insist in solving the problem they way you presented, then you can dynamically configure the job depending on parameters and creating one step for each client using JavaConf:
#Bean
public Job job(){
JobBuilder jb = jobBuilders.get("job");
for(Client c : clientsToProcess) {
jb.flow(buildStepByClient(c));
};
return jb.build();
}
Again, I strongly advise you not to go this way: ugly, against framework philosophy, hard to maintain, debug, you'll probably have to also use JTA/XA here, ...
I hope I've been of any help!
Local Partitioning will solve your problem.
In your partitioner, you will put all of your clients Ids in map as shown below ( just pseudo code ) ,
public class PartitionByClient implements Partitioner {
#Override
public Map<String, ExecutionContext> partition(int gridSize) {
Map<String, ExecutionContext> result = new HashMap<>();
int partitionNumber = 1;
for (String client: allClients) {
ExecutionContext value = new ExecutionContext();
value.putString("client", client);
result.put("Client [" + client+ "] : THREAD " + partitionNumber, value);
partitionNumber++;
}
}
return result;
}
}
This is just a pseudo code. You have to look to detailed documentation of partitioning.
You will have to mark your reader , processor and writer in #StepScope ( i.e. which ever part needs the value of your client ). Reader will use this client in WHERE clause of SQL. You will use #Value("#{stepExecutionContext[client]}") String client in reader etc definition to inject this value.
Now final piece , you will need a task executor and clients equal to concurrencyLimit will start in parallel provided you set this task executor in your master partitioner step configuration.
#Bean
public TaskExecutor taskExecutor() {
SimpleAsyncTaskExecutor simpleTaskExecutor = new SimpleAsyncTaskExecutor();
simpleTaskExecutor.setConcurrencyLimit(concurrencyLimit);
return simpleTaskExecutor;
}
concurrencyLimit will be 1 if you wish only one client running at a time.
I need to trigger updates of a whole bunch of records in a Salesforce database without really updating any values. This is to make a few formulas to recalculate some fields.
Here's what I tried - a schedulable class (say I want it to run every night):
global class acmePortfolioDummyUpdate implements Schedulable
{
global void execute(SchedulableContext SC)
{
for (Acme_Portfolio__c p : [Select Id From Acme_Portfolio__c]) {
update(p);
}
}
}
update(p) is a DML statement and Salesforce limits the number of them to 150. In my case it's about a few thousands of records.
Also, I need to do this across many different portfolios. SF limits the number of scheduled classes to 10.
Any workaround for this?
Thanks
Try Batch Apex. You can schedule Your batch using schedulable class. Correct me if I'm wrong but aren't formulas recalculated each time You read them?
Edit: Comment don't have enought space.
I'm not guarantee this will compile (dont have access to org right now), but try sth like this:
global class batchClass implements Database.batchable<sObject>{
global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator('Select Id From Acme_Portfolio__c');
}
global void execute(Database.BatchableContext BC, List<sObject> scope){
update scope;
}
global void finish(Database.BatchableContext BC){
}
}
And run this from system log:
Database.executeBatch(new batchClass());