Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
A 3D puzzle piece with a lock icon representing PR validation performance and metadata security.
Apex

PR Validation 10x Slower: Solving Permission Set Bottlenecks

Why the metadata engine slows down when Permission Set changes ride along with Apex classes in a Salesforce CI/CD pipeline, and how to decouple the two deployment tracks for faster release cycles.

The short answer

Shipping PermissionSet metadata alongside Apex classes makes the metadata engine revalidate the security model: it recalculates permission set groups and rebuilds the permission cache for every System.runAs test context. Split the pipeline so security metadata deploys separately from application logic.

Key takeaways Avoid bundling. Do not include PermissionSet or PermissionSetGroup metadata in the same deployment as your core Apex business logic. Every time you touch a PSG in metadata, Salesforce triggers a background recalculation job that effectively "locks" the permission state for that org. Heavy use of System.runAs() combined with frequent security metadata updates forces the platform to rebuild permission caches, causing serious latency in your CI/CD pipeline. Maintain separate manifest files for security and logic to keep validation times lean and predictable. Use the Tooling API to check whether your deployments are hanging on security components, and adjust your release cadence accordingly.

PR Validation 10x Slower: Solving Permission Set Bottlenecks

If your Pull Request (PR) validation times in Salesforce have ballooned from a few minutes to nearly an hour, you are not alone. As orgs grow in complexity, the interaction between metadata deployments and the platform's security model turns into a performance problem that is easy to miss. Bundling PermissionSet metadata (especially classAccesses) with Apex class deployments puts the Salesforce metadata engine under the heaviest strain.

The anatomy of the bottleneck

When you include PermissionSet or PermissionSetGroup metadata in a deployment alongside Apex classes, the Salesforce metadata API has to do a heavy lift. Salesforce does not simply "move" the XML files; it validates the integrity of the security model across your entire org.

The calculation chain

  1. Metadata parsing. The platform parses the new classAccesses entries in your Permission Set XML.
  2. PSG recalculation. If those Permission Sets belong to a Permission Set Group (PSG), Salesforce starts an asynchronous recalculation job so that the effective permissions of the user reflect the new state.
  3. Apex dependency check. The compiler verifies that every class being deployed or modified has the required security context. If your tests use System.runAs(), the platform is forced to generate a synthetic user session that honors these new permissions.

When these processes collide, the metadata lock is held significantly longer, and runAs test execution slows sharply as the CPU constantly re-evaluates the permission cache for every test context.

The runAs and test context trap

Developers often overlook how System.runAs() makes performance worse during deployment validation. When you execute code within runAs in a test class, the platform checks the current user's permission set cache.

@isTest
private class PermissionPerformanceTest {
    @isTest
    static void testAccessWithNewPermissions() {
        User testUser = [SELECT Id FROM User WHERE Alias = 'perf'];
        
        // In a deployment, this triggers a permission cache lookup.
        // If PSG/PermissionSet metadata is currently being updated,
        // this call can wait on a metadata lock.
        System.runAs(testUser) {
            MyServiceClass.executeSensitiveLogic();
        }
    }
}

If your deployment includes changes to the classAccesses of the Permission Set assigned to testUser, the security engine may be forced to flush and rebuild the cache repeatedly while the test suite runs. That is the primary driver of the "10x slower" phenomenon.

Decoupling metadata deployments

To restore your pipeline performance, use a "decoupled metadata" strategy: treat security metadata as a separate release tier from your application logic.

Strategy 1: The deployment split

Instead of one massive deployment, split your PRs into two distinct pipelines:

  • Pipeline A (logic): Apex classes, triggers, and Lightning components. No security metadata changes.
  • Pipeline B (security): Permission Sets, PSGs, and Profiles. Run it only when security configuration changes are required.

Strategy 2: Use Permission Sets instead of Profiles

Avoid profile-level access modifications where you can. Profile metadata is heavy because it includes page layout assignments, field-level security, and tab settings. Migrating your security to dedicated PermissionSets reduces the surface area of metadata that has to be recalculated during Apex deployments.

Strategy 3: Optimize runAs for tests

Rather than testing full permission sets in every single unit test, use a "Permission Service" pattern to mock permissions where that is valid, or limit the scope of your runAs blocks.

public class SecurityMockUtil {
    // Use this to check access instead of relying on complex runAs scenarios
    // during bulk testing to speed up validation
    public static Boolean hasAccess() {
        return FeatureManagement.checkPermission('Allow_Sensitive_Feature');
    }
}

Monitoring the metadata lock

If you suspect a specific deployment is hitting a wall, query MetadataDeployStatus through the Tooling API to see whether your validation spends most of its time in the Pending or InProgress state on security components.

// Querying deployment status via Tooling API
SELECT Id, Status, NumberComponentsTotal, NumberComponentsDeployed 
FROM MetadataDeployStatus 
WHERE CreatedById = 'YOUR_USER_ID' 
ORDER BY CreatedDate DESC LIMIT 1

A high NumberComponentsTotal with Status stuck at InProgress for extended periods points to PSGs in your file manifest (package.xml) that have many members. The larger the PSG, the longer the recalculation delay.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment