Use Apex Cursors for Expanded SOQL Query Result Support

Apex cursors allow you to process large query results without returning the entire result set in a single transaction. With cursors, you can traverse query results in parts and navigate forward and back. This makes it easier for developers to work with high-volume and resource-intensive jobs. Cursors are a great alternative to batch Apex as they can be used in a chain of queueable Apex jobs and are more powerful in many ways. They are especially useful for package and advanced developers who regularly work with large query result sets.

NOTE: This feature is a Beta Service, which applies to all the salesforce editions.

Apex cursors are stateless and generate results from the offset specified in the Cursor. fetch(integer position, integer count) method. You must track the offsets or positions of the results within your particular processing scenario.

A cursor is created when a SOQL query is executed on a Database.getCursor() or Database.getCursorWithBinds() call. When a Cursor. fetch(integer position, integer count) method is invoked with an offset position, and the count of records to fetch, the corresponding rows are returned from the cursor. The maximum number of rows per cursor is 50 million, regardless of whether the operation is synchronous or asynchronous. To get the number of cursor rows returned from the SOQL query, use Cursor.getNumRecords().

Apex cursors throw these new System exceptions: System.FatalCursorException and System.TransientCursorException. Transactions that fail with the System.TransientCursorException can be retried.

Apex cursors have the same expiration limits as API Query cursors.

To get Apex cursor limits, use these new methods in the Limits class.

Limits.getApexCursorRows() and its upper bound Limits.getLimitApexCursorRows() method

Limits.getFetchCallsOnApexCursor() and its upper bound Limits.getLimitFetchCallsOnApexCursor() method

These Apex governor limits have been updated with this feature.

Maximum number of rows per cursor: 50 million (both synchronous and asynchronous)

Maximum number of fetch calls per transaction: 10 (both synchronous and asynchronous) 

Maximum number of cursors per day: 10,000 (both synchronous and asynchronous)

Maximum number of rows per day (aggregate limit): 100 million

Salesforce Example

public class QueryChunkingQueuable implements Queueable {
    private Database.Cursor locator;
    private integer position;

    public QueryChunkingQueuable() {
        locator = Database.getCursor
                  ('SELECT Id FROM Contact WHERE LastActivityDate = LAST_N_DAYS:400');
        position = 0;
    }

    public void execute(QueueableContext ctx) {
        List<Contact> scope = locator.fetch(position, 200);
        position += scope.size();
        // do something, like archive or delete the scope list records
        if(position < locator.getNumRecords() ) {
            // process the next chunk
            System.enqueueJob(this);
        }
    }

}

References -

Apex cursors (Beta)

API Query Cursor Limits

Comments

Popular posts from this blog

Introduction to Salesforce Agent Script: Build Predictable AI Agents

Anti-Patterns in Salesforce Agentforce: What to Avoid When Building AI Agents

MCP vs. Traditional APIs: Choosing the Right Integration Approach