1.1 Setup vs Non-Setup Objects (Mixed DML)
Concept
Salesforce partitions sObjects into setup objects (User, Group, GroupMember, PermissionSet, PermissionSetAssignment, QueueSObject, UserRole, etc.) that configure the org itself, and non-setup objects (Account, Case, custom objects) that hold business data.
Because setup DML can change who is allowed to see what mid-transaction, the platform forbids mixing the two kinds of DML in a single transaction — raising the MIXED_DML_OPERATION exception. The standard fix is to move one side of the operation into a separate transaction context using an asynchronous job (Queueable / @future) or using System.runAs() within test methods.
Core Q&A
A: That is a mixed DML violation: PermissionSetAssignment is a setup object and Account is a non-setup object. Both cannot be committed in one transaction because setup changes can alter security evaluation of the same transaction's data.
- In production code, the fix is to isolate the setup DML into its own transaction by enqueueing a
Queueable(or@future) job that performs thePermissionSetAssignmentinsert after the business-data transaction commits. System.runAs()is NOT a production option — it is available exclusively in test contexts.- Architectural Pattern: User and permission provisioning should exist as a dedicated asynchronous service rather than a synchronous side-effect inside business-record triggers.
Follow-ups (Scenario-Based)
A1: A few sObjects have specific exemptions or partial behaviors:
- For example, you can perform DML on
FeedItemalongside non-setup objects, andUserinserts can sometimes be mixed if the insert does not modify fields affecting sharing or roles. - However, relying on these edge cases is fragile; architects should treat
Useras a strict setup object. - Inside Batch Apex, each
execute()batch invocation is its own discrete transaction, but mixed DML is still prohibited within a singleexecute()batch. The solution remains identical: enqueue or chain aQueueablejob from the batch method.
A2: Solve this through pipeline sequencing rather than code workarounds:
- Phase 1: Provision Users, Roles, and Permission Set assignments first using the Bulk API / Data Loader as an isolated setup job.
- Phase 2: Migrate business data in a subsequent load referencing the pre-existing User IDs for record ownership.
- Runtime Provisioning: Any runtime user creation (e.g., auto-creating a user when an assessment record is submitted) should be dispatched to asynchronous
Queueablejobs so business triggers never touch setup objects synchronously.
Recommended Reading: Profiles vs Permission Sets vs Permission Set Groups