Apex Cursor

Atin Varshneya Headshot
Lead Salesforce Developer
  • Twitter
  • LinkedIn

Apex Cursors, introduced as a beta feature in Salesforce Summer ’24, provide a smarter way to handle large SOQL result sets. Instead of returning all records at once—as standard SOQL queries do—Apex Cursors retrieve data in smaller batches, making the process more efficient and controlled. This incremental data retrieval reduces memory usage, improves performance, and enables developers to work with very large datasets without hitting governor limits or experiencing performance issues.

 

Benefits of Apex Cursors Over Batch Apex

1. Bidirectional Navigation

Apex Cursors allow developers to move both forward and backward through query results, whereas Batch Apex processes data only in a forward direction.

2. Single-Transaction Processing

Cursor operations occur within a single transaction, ensuring a consistent data state and reducing the complexity associated with Batch Apex’s multi-transaction model.

3. Queueable Chaining Support

Cursors can be used within Queueable Apex and chained across multiple jobs. This flexibility is not available with Batch Apex.

4. More Predictable Execution

Since cursors operate within a single transaction, execution tends to be more predictable compared to Batch Apex, where batches may run at different times depending on system load.

5. Reduced Governor Limit Constraints

Batch Apex has strict limits such as maximum batch size, number of batches, and long-running transaction constraints. Cursors help avoid many of these limitations by retrieving data in controlled chunks.

6. Simplified Logic

Cursors require less boilerplate code because they do not need separate start, execute, or finish methods. This makes the overall implementation easier to write and maintain.

7. Better for Sequential Data Processing

For use cases that require step-by-step or position-based processing, cursors offer finer control compared to Batch Apex’s segmented execution model.

 

Key Points to Understand About Apex Cursors

1. Apex Cursor Creation

An Apex Cursor is created when a SOQL query is executed using Database.getCursor() or Database.getCursorWithBinds(). These methods return a cursor that points to the result set of the query.

2. Fetching Records with Apex Cursor

Records are retrieved using Cursor.fetch(position, count).

position = starting row

count = number of rows to return

Apex Cursors can return up to 50 million rows, whether the operation runs synchronously or asynchronously.

3. Understanding Limits

Apex Cursors follow the same expiration rules as API query cursors. Salesforce provides new limit methods to help monitor cursor usage:

Limits.getApexCursorRows() and Limits.getLimitApexCursorRows()

Limits.getFetchCallsOnApexCursor() and Limits.getLimitFetchCallsOnApexCursor()

These methods help track how many rows and fetch calls have been used in the current transaction.

4. Exceptions to Know

Apex Cursors can throw two system exceptions:

System.FatalCursorException – indicates a non-recoverable error

System.TransientCursorException – indicates a temporary issue where the transaction can be retried

 

Example: Using Apex Cursors in a Queueable Class

The following example demonstrates how Apex Cursors can retrieve records in chunks and process them using a Queueable Apex job.

public class QueryChunkingQueueable implements Queueable {

    private Database.Cursor locator;

    private Integer position;

    public QueryChunkingQueueable() {

        // Create the cursor using a SOQL query

        locator = Database.getCursor(

            'SELECT Id FROM Contact WHERE LastActivityDate = LAST_N_DAYS:400'

        );

        position = 0;

    }

    public void execute(QueueableContext ctx) {

        // Fetch the next 200 records starting from the current position

        List<Contact> scope = locator.fetch(position, 200);

        // Move the cursor forward based on the number of records returned

        position += scope.size();

        // Perform business logic here

        // Example: process contacts, update records, etc.

        // If more records remain, chain the next Queueable job

        if (position < locator.getNumRecords()) {

            System.enqueueJob(this);

        }

    }

}

How This Example Works:

  • A cursor is created using Database.getCursor(), pointing to the result set of the SOQL query.
  • Each job fetches 200 records at a time using fetch(position, 200).
  • The position moves forward based on the number of records returned.
  • If more rows remain, the Queueable job is chained and the next chunk is processed.
  • This process continues until all records are retrieved through the cursor.

This approach is ideal for large datasets where fine-grained control, predictable execution, and efficient chunk-based processing are required.

 

Conclusion

Apex Cursors provide a modern and efficient way to work with very large SOQL result sets by retrieving data in small, controlled chunks. They reduce memory usage, bypass several Batch Apex limitations, support bidirectional navigation, and offer predictable execution within a single transaction. As a result, they are well suited for large-scale, sequential data processing with simpler code and improved performance.