Enhancing Efficiency and Reliability by Sling jobs


Sling Jobs in AEM serve as a valuable mechanism for handling asynchronous and background tasks. These jobs are particularly beneficial for managing long-running or resource-intensive operations without disrupting the web application’s request-response cycle. They enhance the standard OSGi EventAdmin eventing mechanism by guaranteeing at least once processing, which is essential for use cases like sending notifications, post-processing content, and managing workflow steps.

Here’s an explanation of Sling Jobs with an example:

Suppose you have an AEM website, and you want to generate a sitemap.xml file for SEO purposes. This task involves crawling your site, collecting URLs, and creating an XML file. It could be resource-intensive and time-consuming, making it unsuitable for processing within a web request. This is a perfect use case for Sling Jobs.

How Sling Scheduled Jobs Ensure Execution Reliability

In a clustered environment, the scenario often involves a scheduler initiating a job, but a node may experience downtime during the job’s execution. In such cases, the job could be prematurely interrupted, leading to execution failure and potential data or content inconsistencies. Sling Jobs, however, provide a built-in mechanism for job retries in case of failure.

Here’s how it works: When a job is assigned, it is persisted at the location /var/eventing/jobs. During execution, if the job encounters an issue, it signals a failure by returning a “false” status. This signals the job to be rescheduled. Conversely, a “true” status is returned upon successful completion. Without a maximum retry limit set, the job will automatically reschedule itself and attempt execution again when the system is operational.

It’s important to note that when scheduling tasks, it’s advisable to break them into smaller, quickly executable units. This reduces the risk of job failures, particularly for longer-running tasks.

Sling Jobs Considerations:

Lets dive into considerations while implementing Sling obs to maintain data consistency and job reliability:

  1. Cluster-Ready Task Management:

AEM Cloud Service relies on clustered environments for improved performance. To maximize efficiency, developers must craft code that acknowledges cluster setups, with a particular focus on write operations. The utilization of queue data is essential to generate cluster-ready Sling Jobs, which, in turn, guarantees that write operations occur just once in a specific repository location, preserving data consistency.

2. Managing Prolonged Background Processes:

Long-running background processes are generally not recommended due to the possibility of interruptions caused by potential pod restarts. However, essential tasks, such as third-party data imports involving repository write operations, may necessitate their execution. It is advisable to initiate these tasks on the author instance and then replicate their outcomes to the publish service.

3. Navigating Multiple Processing Scenarios and Deployment Challenges:

Sling Jobs, though generally reliable, face challenges in guaranteeing “exactly-once” processing, particularly during crashes. Job consumers should be designed to handle potential multiple processing instances, and the absence of a job consumer for a specific task is considered a deployment error.

4. Optimizing Queue Configurations:

In AEM Cloud Service, Sling Jobs are initially routed to the primary queue. To optimize job processing, it is crucial to configure key settings such as queue type, maximum retries, queue priority, preferred creation instance, and maximum parallel jobs. Wise configuration choices are essential for ensuring efficient job execution and management.

5. Scheduling Jobs on Author Instances:

Publish instances in AEM operate independently and do not establish direct communication between each other. In AEMaaCS the publish is read-only and quantity of instances may fluctuate. Whenever feasible, it is advised to perform activities on the author instance.

Alternatives for scheduling jobs on publish instance in AEM 6.5

In AEM 6.5, when scheduling Sling jobs on publish instances, two alternative methods can be considered:

Custom Run Mode Strategy:

  • Create a new run mode in AEM 6.5.
  • Configure one of your publish instances to operate with this custom run mode.
  • Trigger jobs based on the criteria of this run mode.
  • Ensure that the chosen publish instance consistently remains operational. Alternatively, have a backup instance configured with the same run mode to handle tasks during downtime.

Instance Election for Job Execution:

Utilize the TopologyEventListener to gather the Sling ID associated with each instance in a clustered environment. Elect the leader instance by considering the Sling IDs. Sample code available here

Choosing Between Sling Jobs and Sling Events:

In Adobe Experience Manager (AEM), it’s crucial to understand when to implement functionality using Sling Jobs and when to use Sling Events. Each has its strengths and is suited for different use cases. Let’s explore these distinctions with examples:

Sling Jobs:

  1. Guaranteed Processing:
    • Use Case: Sending Email Notifications
    • Explanation: When you need to ensure that email notifications are sent reliably, even in the face of system restarts or crashes, Sling Jobs are the ideal choice. Job consumers can handle email dispatch, and retries can be implemented to guarantee delivery.
  2. Resource-Intensive Operations:
    • Use Case: Image or Document Thumbnail Generation
    • Explanation: Resource-intensive tasks, such as generating sitemaps, can be offloaded to Sling Jobs. This prevents blocking the main request-response cycle, ensuring a responsive user experience.

Sling Events:

  1. Lightweight and Non-Guaranteed Processing:
    • Use Case: Logging User Activities
    • Explanation: Sling Events are suitable for lightweight operations, such as logging user activities or other non-critical tasks. These events are fired and processed without the same level of reliability and guaranteed processing as Sling Jobs.

Caution about Sling Commons Scheduler / Sling Events / JCR Events:

The use of the Sling Commons Scheduler for scheduling should be minimized, as execution cannot be guaranteed. It is more likely that it is scheduled, but execution is not ensured. Similarly, when dealing with asynchronous events triggered by observations (JCR events or Sling resource events), caution must be exercised. These events can’t be guaranteed to execute and should be used judiciously.

How Sling Jobs Work:

  1. Job Creation: You create a Sling Job by defining a job with specific properties and parameters.
  2. Job Submission: You submit the job to the Sling Job Manager. It doesn’t run immediately but gets added to a job queue.
  3. Job Execution: A separate thread pool or set of workers handles the execution of Sling Jobs. These threads or workers are managed by AEM.

How Sling Jobs Get Triggered:

Sling Jobs can be triggered in following ways:

1. Scheduled Jobs:

You can schedule Sling Jobs to run at specific times or intervals. For our sitemap example, you could set up a daily or weekly schedule to ensure your sitemap is always up to date.

These jobs are added to the job queue at the scheduled time and can be persisted, surviving server restarts. Here’s an example of creating and managing scheduled jobs in AEM:

Let’s consider that we need to schedule the automatic archival of older content to maintain a clean and organized repository. Here’s how to create a scheduled job in AEM to accomplish this task:

@Component(immediate = true)
// Job Scheduler
public class ContentArchivalJobSchedular {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static final String TOPIC = "content/archival/scheduled";

    @Reference
    private JobManager jobManager;

    @Activate
    protected void activate() {
        //Call this if you want to change schedule of the job. It will unregister and register again
        //stopScheduledJob();
        startScheduledJob();
    }

    public void stopScheduledJob() {
        Collection<ScheduledJobInfo> myJobs = jobManager.getScheduledJobs(TOPIC, 10, null);
        myJobs.forEach(sji -> sji.unschedule());
    }

    public void startScheduledJob() {
        // Check if the scheduled job already exists.
        Collection<ScheduledJobInfo> myJobs = jobManager.getScheduledJobs(TOPIC, 1, null);
        if (myJobs.isEmpty()) {
            //Setting some properties to pass to the JOb
            Map<String, Object> jobProperties = new HashMap<>();
            jobProperties.put("customParam", "Some_custom_value");

            // Daily invocation not yet scheduled
            JobBuilder jobBuilder = jobManager.createJob(TOPIC);
            jobBuilder.properties(jobProperties);

            ScheduleBuilder scheduleBuilder = jobBuilder.schedule();
//            scheduleBuilder.cron("0 * * * * ?"); // Every minute
            scheduleBuilder.daily(0, 0);// Execute daily at midnight

            if (scheduleBuilder.add() == null) {
                // Something went wrong here, use scheduleBuilder.add(List<String>) instead to get further information about the error
            } else {
                logger.info("Job scheduled for: {}", TOPIC);
            }
        }
    }
}

Complete code: ContentArchivalJobSchedular.java

In the code above, we create a scheduled job using the jobManager.createJob() method and specify a topic (“content/archival/scheduled”). We then check if the job is already scheduled using jobManager.getScheduledJobs(). If not, we use the ScheduleBuilder to set the daily schedule for midnight and add the job to the queue using scheduleBuilder.add()

Here’s a sample Job Consumer that can be used with the provided Job Scheduler to handle content archival tasks:

@Component(service = JobConsumer.class, immediate = true,
        property = {
                JobConsumer.PROPERTY_TOPICS + "=" + "content/archival/scheduled"
        })
public class ContentArchivalJobConsumer implements JobConsumer {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public JobResult process(Job job) {
        // Logic for processing content archival job
        // You can access job properties and execute the archival process here

        // Check for any custom parameters in the job
        String customParam = (String) job.getProperty("customParam");
        logger.info("Custom Param: {}", customParam);

        // Your content archival logic here

        /**
          * Determine the appropriate JobResult based on the outcome of the task:
          * OK: Successful processing.
          * FAILED: Unsuccessful processing with rescheduling, keeping the job for the next retry.
          * CANCEL: Unsuccessful processing with no rescheduling.
          * ASYNC: Process using the JobConsumer.AsyncHandler interface.
        */        
        return JobResult.OK;
    }
}

Complete code: ContentArchivalJobConsumer.java

In this example, we create a Job Consumer component that listens for jobs on the “content/archival/scheduled” topic. When a scheduled job is triggered, this Job Consumer will execute the content archival logic. You can access any job properties or custom parameters and process the archival task accordingly. If the processing is successful, return JobResult.OK to indicate successful completion.

Stopping a Scheduled Job:

Scheduled jobs can be unscheduled when they are no longer needed. In the example below, we retrieve all scheduled jobs for a specific topic and unschedule them.

public void stopScheduledJob() {
    Collection<ScheduledJobInfo> myJobs = jobManager.getScheduledJobs(TOPIC, 10, null);
    myJobs.forEach(sji -> sji.unschedule());
}

This method retrieves scheduled jobs for the same topic (“content/archival/scheduled”) and unschedules them using sji.unschedule().

In AEM, scheduled jobs are internally managed using the Commons Scheduler Service. They are persisted by default below /var/eventing/scheduled-jobs and will automatically be added to the job queue by the JobManager when the scheduled time is reached. Note that scheduled jobs are only automatically unscheduled when they are scheduled for a specific date; periodic jobs using a CRON expression need to be manually removed when they are no longer needed.

Scheduled jobs in AEM are useful for automating routine tasks, such as content publishing, reports generation, and archival operations, at specific times or on a regular basis.

2. Event-Based Triggering:

You can trigger jobs based on specific events.

Let’s consider a content management system where you want to automate the process of rendering and publishing a product catalog based on predefined criteria. Here’s how to create a Sling Job to accomplish this task:

Step 1: Create a Job Consumer

Assume that you have a custom component for generating the product catalog. To create a job consumer for this task, implement the `org.apache.sling.event.jobs.consumer.JobConsumer` interface. This job consumer listens to a specific topic, in this case, “product/catalog/rendering.”

@Component(service = JobConsumer.class, immediate = true, property = {
        JobConsumer.PROPERTY_TOPICS + "=" + "product/catalog/generate"
})
public class ProductCatalogJobConsumer implements JobConsumer {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public JobResult process(final Job job) {
        // Retrieve product catalog details and rendering criteria
        String catalogPath = (String) job.getProperty("catalogPath");
        String criteria = (String) job.getProperty("criteria");
        logger.info("Catalog Path: {}", catalogPath);
        logger.info("Criteria: {}", criteria);

        // Logic for generating the product catalog based on criteria
        // Publish the catalog to the website
        return JobResult.OK;
    }
}

Complete code: ProductCatalogJobConsumer.java

In this example, the ProductCatalogJobConsumer listens for jobs on the “product/catalog/generate” topic. It retrieves properties like the catalog path and criteria from the job and processes the generation logic. After generating the product catalog, it publishes it to the website.

Step 2: Create a Job Producer

Now, create a job producer that initiates the generation and publishing of the product catalog. This can be part of a custom service/component/servlet in your AEM application. Inject the org.apache.sling.event.jobs.JobManager interface to trigger the job.

@Component(service = {Servlet.class},
        property = {
                "sling.servlet.paths=/bin/generateCatalog",
                "sling.servlet.methods=GET",
        })
public class ProductCatalogServlet extends SlingSafeMethodsServlet {

    private static final long serialVersionUID = 1L;

    private final String TOPIC = "product/catalog/generate";

    @Reference
    private JobManager jobManager;

    @Override
    protected void doGet(final SlingHttpServletRequest req,
                         final SlingHttpServletResponse resp) throws ServletException, IOException {
        final Resource resource = req.getResource();
        resp.setContentType("text/plain");
        generateProductCatalog("/a/b", "ABC_criterion");
        resp.getWriter().write("Job Scheduled for: " + TOPIC);
    }

    public void generateProductCatalog(String catalogPath, String criteria) {

        Map<String, Object> jobProperties = new HashMap<>();
        jobProperties.put("catalogPath", catalogPath);
        jobProperties.put("criteria", criteria);

        // Trigger the job to render and publish the product catalog
        jobManager.addJob(TOPIC, jobProperties);
    }
}

Complete code: ProductCatalogServlet.java

In this example, the ProductCatalogServlet injects the JobManager interface and provides a method, generateProductCatalog, which accepts the catalog path and generation criteria. It then triggers the job by adding it to the “product/catalog/generate” topic with the associated properties.

In conclusion, Sling Jobs in Adobe Experience Manager provide a powerful mechanism for handling asynchronous and background tasks, offering scalability, reliability, and automation benefits. By understanding when to leverage Sling Jobs and when to use Sling Events, you can ensure the efficient and dependable operation of your AEM application.

Leave a comment