5
votes

I have a scheduled task in my web-app developed using spring boot. I run it on a tomcat cluster, so at hour X the scheduled task start from every node.

I read about: https://github.com/lukas-krecan/ShedLock, so I followed the guide, but it doesn't work.. Here what I have done:

I included these dependency in my pom:

  <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-spring</artifactId>
        <version>0.18.2</version>
    </dependency>       
    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-provider-jdbc</artifactId>
        <version>0.18.2</version>
    </dependency>

Then I added this to my method:

@Transactional(value="transactionManagerClienti",readOnly=false)
@Scheduled(cron="0 03 7,10,13,15 * * MON-FRI")
@SchedulerLock(name = "syncCliente"
@Override
public void syncCliente() {
  ....
}

then where I config the datasource I did:

@Configuration
@EnableJpaRepositories(basePackages = {"it.repository"}, entityManagerFactoryRef="entityManager", transactionManagerRef="transactionManager")
public class DataSourceMuxConfig {

    @Autowired
    private Environment environment;

    @Primary
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mux")
    public DataSource dataSource() throws NamingException {
        if(Arrays.asList(environment.getActiveProfiles()).contains("dev")) {
            return DataSourceBuilder.create().build();
        }else {
            Context ctxConfig = new InitialContext();
            return (DataSource) ctxConfig.lookup("java:comp/env/jdbc/mux");
        }
    }

    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcLockProvider(dataSource);
    }

and this my schedule config:

@Configuration
@EnableScheduling
@EnableAsync
public class SchedulerConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }

    @Bean
    public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
            .withLockProvider(lockProvider)
            .withPoolSize(10)
            .withDefaultLockAtMostFor(Duration.ofMinutes(10))
            .build();
    }

}

but it doesn't work.

Each node of the cluster excute at the same time this scheduled task. Why?

How can I avoid to execute multiple times a task at the same time with spring boot?

1
wheres your lock-for-at-least? If that job completes quick enough two could theoretically run.Darren Forsythe
@DarrenForsythe I'll add it, but my task lasts 10 minutes.. maybe moreDroide
make sure to lock it for the minimum amount of time you would want 1 runDarren Forsythe
@DarrenForsythe I want that only once it will be exectued.. So I added lock-for-at-least=10000... To test it I create another scheduled task, with the same annotation, but they start at the same time.Droide
and have you tried multiple instances of the same application trying to run the exact same schedule task. I'd also check if the transactional + shedlock proxying is interferring with the executionDarren Forsythe

1 Answers

1
votes

I've recently implemented a simple annotation library, dlock, to execute a scheduled task only once over multiple nodes. You can simply do something like below.

@Scheduled(cron = "59 59 8 * * *" /* Every day at 8:59:59am */)
@TryLock(name = "emailLock", owner = NODE_NAME, lockFor = TEN_MINUTE)
public void sendEmails() {
  List<Email> emails = emailDAO.getEmails();
  emails.forEach(email -> sendEmail(email));
}

Please see the Github page for full configuration.