LogoLogo
timvero.comMaven Repository
  • timveroOS SDK guide
  • timveroOS how-to
  • timveroOS admin-side setup
  • Campaigns in timveroOS
    • 7.11
      • Business Context
      • Introduction
      • Core Concept
        • Entity Diagram
        • Lifecycle Diagram
        • Expression
      • Design and Development of the Campaign Model
        • Campaign model
        • Campaign repository
        • Campaign forms
        • Campaign сontroller
        • UI elements
        • Campaign actions
      • Design and Development of the CampaignExecution Model
        • CampaignExecution model
        • CampaignExecution repository
        • CampaignExecution controller
        • UI elements
      • Expression
      • Services
        • CampaignService
        • CampaignExecutionService
      • Campaign Processing
        • Producer
        • Consumer
      • Post-processing
        • Checker
Powered by GitBook
On this page

Was this helpful?

  1. Campaigns in timveroOS
  2. 7.11
  3. Campaign Processing

Producer

πŸ“Œ Purpose

The CampaignExecutionProducer component is responsible for automatically creating CampaignExecution instances based on a preconfigured schedule. It periodically checks campaigns with status SCHEDULED and triggers execution if the time has come.


πŸ”§ Class: CampaignExecutionProducer

@Component
public class CampaignExecutionProducer {

    @Autowired
    private CampaignRepository campaignRepository;
    @Autowired
    private CampaignExecutionService executionService;
    @Autowired
    private TransactionTemplateBuilder ttb;

    @SchedulerLock(name = "CampaignExecutionProducer_produce", lockAtMostFor = "PT5M", lockAtLeastFor = "PT30S")
    @Scheduled(initialDelay = 120_000, fixedDelayString = "${campaign.execution.producer.fixedDelay}")
    public void produce() {
        final LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
        campaignRepository.findAutomaticScheduledCampaigns()
            .forEach(id -> {
                ttb.requiresNew().executeWithoutResult(s -> {
                    Campaign campaign = campaignRepository.getSync(id);
                    if (campaign.isActive()
                        && campaign.getAutomaticCampaignStatus() == AutomaticCampaignStatus.SCHEDULED
                        && isWaitNewExecution(campaign, now)) {
                        executionService.createExecution(campaign);
                        campaignRepository.saveAndFlush(campaign);
                    }
                });
            });
    }

    // Helper methods below...
}

⏰ Execution Frequency

The @Scheduled annotation launches the produce() method at intervals defined in configuration:

campaign.execution.producer.fixedDelay=60000  # Example: every 60 seconds

The @SchedulerLock annotation (via Shedlock) ensures the method runs in only one service instance in a clustered deployment.


🧠 Conditions for Creating a New CampaignExecution

The produce() method creates a new execution only if all of the following are true:

  • The campaign is active

  • Its status is SCHEDULED

  • The current time now is equal to or after dateTimeExecution

  • Depending on the restartable flag, one of the following applies:

    • All previous executions are DISABLED

    • The next scheduled interval has arrived (based on restart configuration)


πŸ” Support for restartable Campaigns

βœ… If restartable = false

private boolean isCreateForNotRestartable(Campaign campaign) {
    return !campaign.isRestartable()
        && campaign.getExecutions().stream().allMatch(e -> e.getStatus() == DISABLED);
}

Only one execution is allowed β€” further executions require all previous ones to be disabled.


πŸ” If restartable = true

private boolean isCreateForRestartable(Campaign campaign, LocalDateTime now) {
    Duration interval = campaign.getRestartableDuration(); // hours, days
    LocalDateTime current = campaign.getDateTimeExecution();

    while (!current.plus(interval).isAfter(now)) {
        current = current.plus(interval);
    }

    LocalDateTime nextStart = current;

    return campaign.getExecutions().stream().allMatch(e -> ofInstant(e.getCreatedAt()).isBefore(nextStart))
        || campaign.getExecutions().stream().filter(e -> !ofInstant(e.getCreatedAt()).isBefore(nextStart))
           .allMatch(e -> e.getStatus() == DISABLED);
}

This enables cyclic execution (e.g., once every hour/day) based on restartableUnit and restartableUnitType.


🧩 Helper Method

private boolean isWaitNewExecution(Campaign campaign, LocalDateTime now) {
    return (now.isEqual(campaign.getDateTimeExecution()) || now.isAfter(campaign.getDateTimeExecution()))
        && (isCreateForNotRestartable(campaign) || isCreateForRestartable(campaign, now));
}

This method consolidates all logic for determining whether a new execution should be created.


πŸ“ Example Behavior

Campaign A:

  • executionType = AUTOMATIC

  • dateTimeExecution = 2025-05-01T09:00

  • restartable = true

  • restartableUnitType = HOURS, restartableUnit = 6

Producer output:

  • May 1, 09:00 β†’ first execution

  • May 1, 15:00 β†’ second execution

  • May 1, 21:00 β†’ third execution

  • ...


πŸ“Œ Summary

  • CampaignExecutionProducer is an automatic campaign scheduler

  • Controlled via @Scheduled and @SchedulerLock

  • Checks campaign status, activation, execution time, and recurrence settings

  • Operates in a new isolated transaction, preventing interference with outer processes

PreviousCampaign ProcessingNextConsumer

Last updated 18 days ago

Was this helpful?