Mixed DML Operation Error in Salesforce

DESCRIPTION

Let’s first understand “Setup Objects” and Non-setup objects.

Setup Objects
  • The Setup object in Salesforce is a special object used internally to manage user access to Salesforce setup and configuration pages. It is not an actual object that can be queried or manipulated in the same way as standard or custom objects like Account, Opportunity, etc. Instead, the term “Setup” typically refers to the settings and administrative interface in Salesforce where administrators can configure various system-wide features.
  • So creating/updating User, Role, Profile, and Permission Set are considered as manipulating Setup objects.
Non-setup Objects

non-setup objects refer to objects that are part of the application data model and are used to store and manage business data. These are the objects that you and your users typically interact with on a day-to-day basis to handle records like customer information, sales opportunities, support cases, etc.

MIXED DML OPERATION PROBLEM

As we know setup objects (such as profiles, users, or roles) and non-setup objects (such as accounts, contacts, or custom objects) uses different databases which can cause errors or inconsistencies in the backend; also changing setup data can impact the whole platform, so keeping it separate from business data ensures the system runs smoothly without issues.

Key Reasons for Keeping Setup and Non-Setup Objects Separate:
  1. Different Databases for Stability:
    • Setup objects (such as User, Profile, Role, ApexClass) are part of the configuration layer of Salesforce, managing system-wide settings and metadata.
    • Non-setup objects (like Account, Contact, Opportunity, CustomObject) deal with business data, which is the core of CRM operations.
    • These two sets of objects are stored in different databases or layers to ensure that changes to configuration settings (setup objects) don’t accidentally impact day-to-day operations involving business data (non-setup objects).
  2. Prevent Errors and Inconsistencies:
    • Changing setup data (e.g., modifying profiles, permissions, or triggers) could inadvertently affect the way users interact with business data. For example, altering a profile’s permissions might restrict access to Account or Opportunity objects.
    • By separating setup objects and non-setup objects, Salesforce reduces the chance of unexpected system errors or inconsistencies caused by these changes.
  3. Impact of Setup Changes on the Entire Org:
    • Setup object changes can affect the entire organization. For instance, updating a permission set could affect multiple users across various departments. Similarly, altering an Apex class or trigger could modify how data is processed for all records.
    • Keeping this configuration data isolated ensures that critical platform settings can be managed carefully without causing widespread disruption to daily operations.
  4. Platform Performance and Scalability:
    • Since setup data often requires less frequent updates but can have a broad impact, and business data (non-setup) is updated and queried frequently, separating them helps ensure that performance bottlenecks do not occur.
    • By keeping the databases separate, Salesforce can optimize the performance of frequent business transactions without impacting the system’s ability to handle administrative and configuration changes.
  5. Transaction Handling and Security:
    • Salesforce transactions involving business data (such as creating new leads, closing opportunities, or updating account information) follow a different transactional path than setup data (such as changing profiles or deploying metadata).
    • By keeping these transactions separate, Salesforce ensures data consistency and security while minimizing the risk of one type of transaction impacting another.
  6. Deployment and Change Management:
    • Setup objects are often deployed using the Metadata API, Change Sets, or Salesforce DX to manage changes in configuration and customization. This deployment is usually done in a controlled way to minimize the risk of disruption.
    • Non-setup objects (business data) are managed through day-to-day CRUD operations by users and applications, and are not typically part of the deployment process.
    • By separating the two, Salesforce ensures that deployments related to configuration (setup data) are handled differently from the ongoing management of business records, reducing the likelihood of system issues.
Example of Potential Errors from Mixing Setup and Non-Setup Data:

Another example would be modifying a Role or Profile that has permissions tied to accessing certain non-setup objects (like Opportunity or Case). A wrong configuration in these setup objects could suddenly restrict user access to critical business data, causing operational issues.

// Example: This will cause a mixed DML error
User newUser = new User(
    LastName = 'Doe',
    Email = 'jdoe@example.com',
    Username = 'jdoe@example.com',
    ProfileId = '00e00000000XXXX',
    Alias = 'jdoe',
    TimeZoneSidKey = 'America/Los_Angeles',
    LocaleSidKey = 'en_US',
    EmailEncodingKey = 'UTF-8',
    LanguageLocaleKey = 'en_US'
);

Account newAccount = new Account(Name = 'New Client Account');

// Attempting to insert both a User (setup object) and Account (non-setup object) in the same transaction
insert newUser;
insert newAccount;  // This will throw a Mixed DML exception

SOLUTION: Use Asynchronous Processing

To work around this, separate the DML operations into different transactions using asynchronous methods such as:

  • @future Methods
  • Queueable Apex
  • Batch Apex
Example Using @future:

Here’s how to separate the setup and non-setup DML operations using the @future method:

// Class to handle User creation asynchronously
public class SetupObjectHandler {
    @future
    public static void createUserAsync(String lastName, String email, String username, Id profileId) {
        User newUser = new User(
            LastName = lastName,
            Email = email,
            Username = username,
            ProfileId = profileId,
            Alias = 'futureuser',
            TimeZoneSidKey = 'America/Los_Angeles',
            LocaleSidKey = 'en_US',
            EmailEncodingKey = 'UTF-8',
            LanguageLocaleKey = 'en_US'
        );
        insert newUser; // Handle DML on setup object
    }
}

// Class to handle non-setup operations in the current transaction
public class NonSetupObjectHandler {
    public static void createAccountAndUser() {
        Account newAccount = new Account(Name = 'New Client Account');
        insert newAccount; // Handle DML on non-setup object
        
        // Now use future method to create User asynchronously
        SetupObjectHandler.createUserAsync('Doe', 'jdoe@example.com', 'jdoe@example.com', '00e00000000XXXX');
    }
}

Summary

When handling both setup and non-setup objects in Apex, it’s important to:

  1. Separate DML operations for setup and non-setup objects by using asynchronous methods like @future, Queueable, or Batch Apex.
  2. Handle setup object operations in tests with System.runAs() to avoid Mixed DML errors.
  3. Check permissions before attempting to create or modify setup objects.
  4. Use Metadata API for large-scale or bulk updates to configuration data.

By following these guidelines, you can ensure that your Apex code handles setup and non-setup object interactions safely and without triggering system errors or performance issues. Let me know if you’d like more specific examples or further clarification!