@RefreshScope stops @Scheduled task

spring refreshscope
spring refresh bean periodically
configurationproperties vs refreshscope
dynamic cron expression from database in spring scheduler
spring scheduler run once
spring boot dynamic properties
spring change property value at runtime

I have a monitoring app wherein I am running a fixedRate task. This is pulling in a config parameter configured with Consul. I want to pull in updated configuration, so I added @RefreshScope. But as soon as I update the config value on Consul, the fixedRate task stops running.

@Service
@RefreshScope
public class MonitorService {

    @Autowired
    private AppConfig appConfig;

    @PostConstruct
    public void postConstRun() {
        System.out.println(appConfig.getMonitorConfig());
    }

    @Scheduled(fixedRate = 1000)
    public void scheduledMonitorScan() {
        System.out.println("MonitorConfig:" + appConfig.getMonitorConfig());
    }
}

AppConfig class just has a single String parameter:

@Configuration
@Getter
@Setter
public class AppConfig {

    @Value("${monitor-config:default value}")
    private String monitorConfig;
}

As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.

I have done workaround for this kind of scenario by implementing SchedulingConfigurer interface. Here I am dynamically updating "scheduler.interval" property from external property file and scheduler is working fine even after actuator refresh as I am not using @RefreshScope anymore. Hope this might help you in your case also.

public class MySchedulerImpl implements SchedulingConfigurer {

    @Autowired
    private Environment env;

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

    @Override
    public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(this.taskExecutor());
        taskRegistrar.addTriggerTask(() -> {
            //put your code here that to be scheduled
        }, triggerContext -> {
            final Calendar nextExecutionTime = new GregorianCalendar();
            final Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();

            if (lastActualExecutionTime == null) {
                nextExecutionTime.setTime(new Date());
            } else {
                nextExecutionTime.setTime(lastActualExecutionTime);
                nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("scheduler.interval", Integer.class));
            }
            return nextExecutionTime.getTime();
        });
    }
}

@RefreshScope and @Scheduled · Issue #567 · spring-cloud/spring , When I use the @RefreshScope annotation to dynamically refresh the internal private properties of the class, if the @scheduled annotation is used in this class or the SchedulingConfigurer interface is used to define the scheduled task, the scheduled task will be invalidated while the property is refreshed. Joy and pain with @Scheduled and @RefreshScope in SpringBoot | TLDR; @Scheduled and @RefreshScope are powerful tools but do not work out of the box together causing dangerous inconsistencies. Find out how to get them to play nicely and more advanced scheduling opporunities.

Here's how we've solved this issue.

/**
 * Listener of Spring's lifecycle to revive Scheduler beans, when spring's
 * scope is refreshed.
 * <p>
 * Spring is able to restart beans, when we change their properties. Such a
 * beans marked with RefreshScope annotation. To make it work, spring creates
 * <b>lazy</b> proxies and push them instead of real object. The issue with
 * scope refresh is that right after refresh in order for such a lazy proxy
 * to be actually instantiated again someone has to call for any method of it.
 * <p>
 * It creates a tricky case with Schedulers, because there is no bean, which
 * directly call anything on any Scheduler. Scheduler lifecycle is to start
 * few threads upon instantiation and schedule tasks. No other bean needs
 * anything from them.
 * <p>
 * To overcome this, we had to create artificial method on Schedulers and call
 * them, when there is a scope refresh event. This actually instantiates.
 */
@RequiredArgsConstructor
public class RefreshScopeListener implements ApplicationListener<RefreshScopeRefreshedEvent> {
    private final List<RefreshScheduler> refreshSchedulers;

    @Override
    public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
        refreshSchedulers.forEach(RefreshScheduler::materializeAfterRefresh);
    }
}

So, we've defined an interface, which does nothing in particular, but allows us to call for a refreshed job.

public interface RefreshScheduler {
    /**
     * Used after refresh context for scheduler bean initialization
     */
    default void materializeAfterRefresh() {
    }
}

And here is actual job, whose parameter from.properties can be refreshed.

public class AJob implements RefreshScheduler {
    @Scheduled(cron = "${from.properties}")
    public void aTask() {
        // do something useful
    }
}

UPDATED: Of course AJob bean must be marked with @RefreshScope in @Configuration

@Configuration
@EnableScheduling
public class SchedulingConfiguration {
    @Bean
    @RefreshScope
    public AJob aJob() {
        return new AJob();
    }
}

If @RefreshScope is added, @Scheduled does not work any more , BUT, the moment I then post to the /refresh endpoint, the schedule task dies/goes on vacation/something (i.e console output stops dead). Current  Spring provides excellent support for both task scheduling and asynchronous method execution based on cron expression using @Scheduled annotation. The @Scheduled annotation can be added to a method along with trigger metadata. In this post, I will show the means to use @Scheduled feature in 4 different ways. Read More : Spring timer tasks Table …

My solution consists of listening to EnvironmentChangeEvent

@Configuration
public class SchedulingSpringConfig implements ApplicationListener<EnvironmentChangeEvent>, SchedulingConfigurer {

  private static final Logger LOGGER = LoggerFactory.getLogger(SchedulingSpringConfig.class);

  private final DemoProperties demoProperties;

  public SchedulingSpringConfig(DemoProperties demoProperties) {
    this.demoProperties = demoProperties;
  }

  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    LOGGER.info("Configuring scheduled task with cron expression: {}", demoProperties.getCronExpression());
    taskRegistrar.addTriggerTask(triggerTask());
    taskRegistrar.setTaskScheduler(taskScheduler());
  }

  @Bean
  public TriggerTask triggerTask() {
    return new TriggerTask(this::work, cronTrigger());
  }

  private void work() {
    LOGGER.info("Doing work!");
  }

  @Bean
  @RefreshScope
  public CronTrigger cronTrigger() {
    return new CronTrigger(demoProperties.getCronExpression());
  }

  @Bean
  public ThreadPoolTaskScheduler taskScheduler() {
    return new ThreadPoolTaskScheduler();
  }

  @Override
  public void onApplicationEvent(EnvironmentChangeEvent event) {
    if (event.getKeys().contains("demo.config.cronExpression")) {
      ScheduledTasksRefresher scheduledTasksRefresher = new ScheduledTasksRefresher(triggerTask());
      scheduledTasksRefresher.afterPropertiesSet();
    }
  }
}

Then I use the ContextLifecycleScheduledTaskRegistrar to recreate the task.

public class ScheduledTasksRefresher extends ContextLifecycleScheduledTaskRegistrar {

  private final TriggerTask triggerTask;

  ScheduledTasksRefresher(TriggerTask triggerTask) {
    this.triggerTask = triggerTask;
  }

  @Override
  public void afterPropertiesSet() {
    super.destroy();
    super.addTriggerTask(triggerTask);
    super.afterSingletonsInstantiated();
  }
}

Properties definition:

@ConfigurationProperties(prefix = "demo.config", ignoreUnknownFields = false)
public class DemoProperties {

  private String cronExpression;

  public String getCronExpression() {
    return cronExpression;
  }

  public void setCronExpression(String cronExpression) {
    this.cronExpression = cronExpression;
  }
}

Main definition:

@SpringBootApplication
@EnableConfigurationProperties(DemoProperties.class)
@EnableScheduling
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

Joy and pain with @Scheduled and @RefreshScope in SpringBoot, | TLDR; @Scheduled and @RefreshScope are powerful tools but do not work out of the box together causing dangerous inconsistencies. Find out  Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on @ Configuration classes as follows: @Configuration @EnableScheduling public class AppConfig { // various @Bean definitions } This enables detection of @ Scheduled annotations on any Spring-managed bean in

@RefreshScope and @Scheduled, When I use the @RefreshScope annotation to dynamically refresh the internal private properties of the class, if the @scheduled annotation is used in this class or the SchedulingConfigurer interface is used to define the scheduled task, the scheduled task will be invalidated while the property is refreshed. Hello. I have a simple Spring Boot application in which I just included the spring-boot-admin-starter-client (version 1.5.2). Everything works fine except for the scheduled method detailed below: @

Spring Cloud, A Spring @Bean that is marked as @RefreshScope will get special treatment when /pause and /resume for calling the Lifecycle methods ( stop() and start() on absolute paths in Windows require an extra "/", e.g. file:///${user.home}/​config-repo Don't use the EurekaClient in @PostConstruct method or in a @​Scheduled  With above conditional flag, we're able to trigger the scheduled Spring Batch job with the scheduled task alive. If we don't need to resume the job, then we can actually stop the scheduled task to save resources. Let's take a look at two options in the next two subsections. 3.1. Using Scheduler Post Processor

1. Spring Cloud Context: Application Context Services, configuration files and has endpoints for common management and monitoring tasks. changes (although you could set it up with a @Scheduled annotation). Refresh scope beans are lazy proxies that initialize when they are used (that is, /actuator/pause and /actuator/resume for calling the Lifecycle methods ( stop()  Running background scheduled tasks in Spring. the task will print out the current time Console and it will stop fixedDelay second after the task is finished to

Comments
  • Have you tried separating them?
  • Separating what? Do you mean move RefreshScope out of MonitorService class?
  • You just need the @RefreshScope in AppConfig, and that will refresh the injected bean in your service.
  • I tried adding @ RefreshScope in AppConfig. Reading Sprind documentation I found that the public refresh method in the annotation is exposed in the /refresh endpoint. So I added spring actuator dependency. This now refreshes my configuration, but my @ Scheduled stops working when I add the spring actuator.