How to force a re-evaluation of the start timer definition?

Hello,

I have a process model with a start timer event of type “Cycle”. The “Timer Definition” property is set via a reference to a spring bean: ${processConfig.startSchedule} (the process application is based on spring). The value for this property is set via spring properties (application.yml).

Now I’d like to change the schedule. I changed the value of the spring property and re-deployed the application. But the schedule does not seem to change.

Am I correct in the assumption that this happens because the evaluation of the timer happens only on a real deployment of the process model? Since my model did not change, no deployment took place so that the timer expression was not evaluated.

If this is correct: How can I force a re-evaluation of the timer expression? I don’t want to make any changes to the process model.

Or are there other reasons for this behaviour?

Thank you for the hints!

Hrm… There was a setting in the process engine that would suppress the model comparison when the model is deployed, so that the model gets definitely deployed. I just can’t recall its name…

Your assumption is correct - with no changes to the model, there is no new deployment and version of the process definition and the originally calculated timer schedule is not re-evaluated. When performing manual deployments, this page describes the options for the REST API, Post Deployment | docs.camunda.org , in particular the options enable-duplicate-filtering and deploy-changed-only would probably be of interest.

On the process engine configuration side, Process Engine Configuration | docs.camunda.org has all the available options for Spring Boot.

The RepositoryService offers you a DeploymentBuilder object when creating a new Deployment, where you can call enableDuplicateFiltering(deployChangedOnly) for an individual deployment.

So it depends on how you currently do your deployments which option you need.

Hi @tiesebarrell ,

thank you for the quick reponse! After it, I looked into the source code of the Rest API and saw that it’s done by explicitly creating an instance of DeploymentBuilder and configuring it.

We don’t do manual deployments; we deploy process applications built with spring boot. We have a virtual machine where we start the spring boot based process application. To deploy a new version, we just shut down the old machine and create a new one which contains the new app version.

Is there a way to configure the spring boot app (camunda deployer within it) so that it definitely deploys the process model (and thus uses the current property values)?

@fml2 Not that I’m aware of. We do the same for some process models, although not with Spring Boot. For those cases when we want to force a redeploy we simply make a change (just anything) to the model to ensure it’s different and gets a new version.

What you could also do is to write a process engine plugin that will run after the engine is created and manually deploy the new process, which, depending on the expression you return from the timer expression, you might be able to make a bit smarter than “just always re-deploy”, if you can somehow check whether the expression has actually started to return a new value. One way to do that with Camunda would be to use the generic property table and insert your own property there. That obviously only works if the expression returns a period rather than an exact moment in time. Also, consider that if you’re running a cluster of engines and they all get updated or restarted, the plugin will trigger on all of them and might re-deploy the process multiple times if you can’t detect whether or not the expression has changed its output.

Yet another approach would be to get access to the ExpressionManager in a ProcessEnginePlugin and re-evaluate the same expression as the process model’s timer uses. You could either hard-code that or even get it from the model by retrieving it from the RepositoryService. Then find the job that was scheduled for the timer and simply update the value of the due date based on the outcome of the expression. Come to think of it, that might be a cleaner solution because you’re simply forcing a re-evaluation of the timing to make sure it’s up to date, but not needlessly redeploying the process model.

Thank you very much for the tips. The approach with updating the timer job is what I think is the best approach. I’ll try it.

I was just wondering whether there is a simpler way.

Okay, good luck; I hope it works out for you :+1:

an easier approach: use Spring Boots @Scheduled with the cron expression fed from your application.yaml. In that, invoke runtimeService.startProcessInstanceByKey. In the BPM model, you’d need to change the Start event from timer-based to simple.

I’ve thought about this. This won’t work in a clustered environment since each node would start a process instance.

Hi, this might be of use to look at

I did that as I also needed to reconfigure start timers

1 Like

Does this work? I.e. does the new schedule take effect after changing it and a restart of the application?

Update: Ah, the main point is that you inject a custom “duedateDescription”, right?

Yes it works, we have used it in a couple of projects now. Thats correct we use a custom CycleBusinessCalendar to look up the duedateDescription from our config. The main thing to remember is on startup to have the job executor disabled until you have recalculated the timers and then enable it

First of all: very cool!

Second: could you please explain how the new schedule definition (cron expression) gets saved in the camunda DB?

IIUC, the main intended branch is the one with if (cronExpression.length == 2) . But there, the duedateDescription (the new schedule) is not passed to a camunda API at all. So it can’t be saved in the DB for the future use. What am I missing?

Third: as has been noted somewhere else, it would be IMO more intuitive if the timer schedule would be automatically updated on startup. What do you think?

Fourth: Where is the definition of the bean process.startTime? Did you omit it for brevity?

So the new definition doesn’t get saved to the db as such. So for example in one of my processes I have the timer defined as

<bpmn:timerEventDefinition id="TimerEventDefinition_07xh7k1">
  <bpmn:timeCycle xsi:type="bpmn:tFormalExpression">process.startTime</bpmn:timeCycle>
</bpmn:timerEventDefinition>

and in the act_ru_job the timer keeps this definition of process.startTime.
Each time camunda works out when the next start time is it will go through the CycleBusinessCalendar but in this case we have extended that functionality with our own. So each time time it takes the timer definition which is process.startTime and look up its current definition, which could come from the application.yml or anywhere else like a db table which could mean you can change the definition without a restart(would be picked up next time the timer is recalculated or you could potentially put something in to force a recalculate of the start timer)

The process.startTime was defined here

@Component
@Getter
public class ProcessConfiguration {
  private static Map<String, String> timerDefinitions = new HashMap<>();

  public ProcessConfiguration(
      @Value("${process.startTime:0 0 19 ? * * TZ Europe/Paris}") String processStartTime) {
    timerDefinitions.put("process.startTime", processStartTime);
  }

  public String getTimerDefinition(String startTimer) {
    return timerDefinitions.get(startTimer);
  }
}

and just in the yaml as a property then picked up with the @Value.

Yes, I understood how it works, thank you for explaining. In your model, the property is set to ${process.startTime} (with the dollar sign and {}), that’s why I asked about the bean. IMO the expression in the process model should be process.startTime (just a string, not a reference).

Sorry, I didn’t notice that I’d done that, and you are correct it should be without the ${} so just process.startTime. It looks like I did correct myself in my previous reply but didn’t realise I’d made that mistake in the other thread.

IIUC this is only necessary if you want the new schedule to have effect immediately. If you don’t do this then the next process start will be done with the old schedule, and the new schdule will be taken into account for the start after that. Right?

Yes, thats right