Sunday, September 17, 2023

“View All” and “Modify All” Permissions in Salesforce

Permission Type: View All and Modify All

The “View All” and “Modify All” permissions ignore sharing rules and settings, allowing administrators to grant access to records associated with a given object across the organization.

Users who need this permission: Delegated administrators who manage records for specific objects.

Permission Type: View All Data and Modify All Data

Users with View All Data (or Modify All Data) permission can view (or modify) all apps and data, even if the apps and data are not shared with them and hence “View All” and “Modify All” can be better alternatives to the “View All Data” and “Modify All Data” permissions.

Permission Type: View All Users

Users who need to see all users in the organization. Useful if the organization-wide default for the user object is Private. Administrators with the Manage Users permission are automatically granted the View All Users permission.

Salesforce batch apex limitations

  1. Up to 5 batch jobs can be queued or active concurrently.
  2. Up to 100 Holding batch jobs can be held in the Apex flex queue.
  3. A maximum of 50 million records can be returned in the Database.QueryLocator object. If more than 50 million records are returned, the batch job is immediately terminated and marked as Failed.
  4. If the start method of the batch class returns a QueryLocator, the optional scope parameter of Database.executeBatch can have a maximum value of 2,000. If set to a higher value, Salesforce chunks the records returned by the QueryLocator into smaller batches of up to 2,000 records. If the start method of the batch class returns an iterable, the scope parameter value has no upper limit. However, if you use a high number, you can run into other limits. The optimal scope size is a factor of 2000, for example, 100, 200, 400 and so on.
  5. If no size is specified with the optional scope parameter of Database.executeBatch, Salesforce chunks the records returned by the start method into batches of 200 records. The system then passes each batch to the execute method. Apex governor limits are reset for each execution of execute.
  6. The start, execute, and finish methods can implement up to 100 callouts each.
  7. Only one batch Apex job's start method can run at a time in an org. Batch jobs that haven’t started yet remain in the queue until they're started. This limit doesn’t cause any batch job to fail and execute methods of batch Apex jobs still run in parallel if more than one job is running.

Salesforce batch apex best practices

  1. Only use Batch Apex if you have more than one batch of records. If you don't have enough records to run more than one batch, you are probably better off using Queueable Apex.
  2. Use extreme care if you are planning to invoke a batch job from a trigger. You must be able to guarantee that the trigger won’t add more batch jobs than the limit.
  3. Methods declared as future aren’t allowed in classes that implement the Database.Batchable interface.
  4. Methods declared as future can’t be called from a batch Apex class.
  5. All implemented Database.Batchable interface methods must be defined as public or global.
  6. Batch jobs queued before a Salesforce service maintenance downtime remain in the queue. After service downtime ends and when system resources become available, the queued batch jobs are executed. If a batch job was running when downtime occurred, the batch execution is rolled back and restarted after the service comes back up.
  7. To implement record locking as part of the batch job, you can re-query records inside the execute() method, using FOR UPDATE, if necessary. This ensures that no conflicting updates are overwritten by DML in the batch job. To re-query records, simply select the Id field in the batch job's main query locator.
  8. Tune any SOQL query to gather the records to execute as quickly as possible.
  9. Batch Apex jobs run faster when the start method returns a QueryLocator object that doesn't include related records via a subquery. Avoiding relationship subqueries in a QueryLocator allows batch jobs to run using a faster, chunked implementation. If the start method returns an iterable or a QueryLocator object with a relationship subquery, the batch job uses a slower, non-chunking, implementation. For example, if the following query is used in the QueryLocator, the batch job uses a slower implementation because of the relationship subquery: SELECT Id, (SELECT id FROM Contacts) FROM Account                                         
  10. A better strategy is to perform the subquery separately, from within the execute method, which allows the batch job to run using the faster, chunking implementation.

Saturday, September 16, 2023

How to handle UNABLE_TO_LOCK_ROW error in salesforce?

This is one of the most common error in salesforce which occurs when multiple users or processes are trying to access and modify the same record simultaneously. 

This can be handle by following the below best practices:

1) While performing bulk operations, consider breaking them into smaller batches. Smaller batches are less likely to encounter locking conflicts. You can make use of Bulk API or Batch Apex, to process large volumes of data in smaller, controlled batches. 

2) You can also make use of FOR UPDATE as shown below to lock sObject records. While an sObject record is locked, no other client or user is allowed to make updates either through code or the Salesforce user interface. The lock gets released when the transaction completes. 

The example queries for two accounts and also locks the accounts that are returned.

Account [] accts = [SELECT Id FROM Account LIMIT 2 FOR UPDATE];

Locking Considerations:

While the records are locked by a client, the locking client can modify their field values in the database in the same transaction. Other clients have to wait until the transaction completes and the records are no longer locked before being able to update the same records. Other clients can still query the same records while they’re locked.

If you attempt to lock a record currently locked by another client, your process waits a maximum of 10 seconds for the lock to be released before acquiring a new lock. If the wait time exceeds 10 seconds, a QueryException is thrown. Similarly, if you attempt to update a record currently locked by another client and the lock isn’t released within a maximum of 10 seconds, a DmlException is thrown.

If a client attempts to modify a locked record, the update operation can succeed if the lock gets released within a short amount of time after the update call was made. In this case, it’s possible that the updates overwrite those made by the locking client if the second client obtained an old copy of the record. To prevent the overwrite from happening, the second client must lock the record first. The locking process returns a fresh copy of the record from the database through the SELECT statement. The second client can use this copy to make new updates.

The record locks that are obtained in Apex via FOR UPDATE clause are automatically released when making callouts. Use caution while making callouts in contexts where FOR UPDATE queries could have been previously executed.

3) You can also implement a retry mechanism in your code so that when this error occurs, you can catch the exception, wait for a short period, and then try the operation again. 

Integer maxAttempts = 3;

Integer retryInterval = 2000; // 2 seconds

 

for (Integer attempts = 0; attempts < maxAttempts; attempts++) {

    try {

        // Perform the operation that might cause UNABLE_TO_LOCK_ROW

        // ...

        // If successful, break out of the loop

        break;

    } catch (Exception e) {

        if (e.getMessage().contains('UNABLE_TO_LOCK_ROW')) {

            // Wait for a moment before retrying

            Thread.sleep(retryInterval);

        } else {

            // Handle other exceptions as needed

            throw e;

        }

    }

}

Saturday, September 9, 2023

Use of salesforce batch apex class with a real time scenario

 Below are the real-time scenarios to use the Apex Batch job.

1) Say if you want to send an email 30 days before the contract expired to notify the owner that the contract is going to expire.

In this situation we will go with Batch Apex, which will run as needed say daily and check all the contracts which are expiring in next 30 days and send an email to the owner person.

Please note we can also create a Time-based workflow to achieve the above requirement however it doesn't run against existing records. The new rule only applies to records created or updated after the rule is activated.

2) Create a renewal opportunity base on contract end date.

3) Delete all the cases which have Status as “Closed” and Closed Reason as “No Action Needed” and Closed Date of Last Year.

Let’s see a practical example. Let’s say you have a business requirement that states that all contacts for which Account Billing Country is Australia must have their other phone number same as that is present on the Account. Unfortunately, users are entering different phone number in the other phone number.

Write a Batch Apex class that ensures that this requirement is enforced.

public class UpdateContactCompanysPhone implements

    Database.Batchable<sObject>, Database.Stateful {

    public Database.QueryLocator start(Database.BatchableContext bc) {

        return Database.getQueryLocator(

            'SELECT ID, Phone, (SELECT ID, OtherPhone FROM Contacts) FROM Account ' +

            'Where BillingCountry = \'Australia\''

        );

    }

    public void execute(Database.BatchableContext bc, List<Account> scope){

        List<Contact> contacts = new List<Contact>();

        for (Account account : scope) {

            for (Contact contact : account.contacts) {

                contact.OtherPhone = account.Phone;

                contacts.add(contact);

            }

        }

        update contacts;

    }

    public void finish(Database.BatchableContext bc){

        System.debug('Finish Executed');

    }

}

 

@isTest

private class UpdateContactCompanysPhoneTestClass {

    @testSetup

    static void setup() {

        List<Account> accounts = new List<Account>();

        List<Contact> contacts = new List<Contact>();

    

        for (Integer i=0;i<1;i++) {

            accounts.add(new Account(name='Account '+i,

                billingcity='Sydney', billingcountry='Australia'));

        }

        insert accounts;

   

        for (Account account : [select id from account]) {

            contacts.add(new Contact(firstname='Test',

                lastname='Contact', accountId=account.id));

        }

        insert contacts;

    }

    @isTest static void test() {

        Test.startTest();

        UpdateContactCompanysPhone updcon = new UpdateContactCompanysPhone();

        Id batchId = Database.executeBatch(updcon);

        Test.stopTest();

 

        System.assertEquals(1, [select count() from contact]);

    }

}

To run the above batch class,

Paste the below code in developer console “Execute Anonymous Window”.

  UpdateContactCompanysPhone updcon = new UpdateContactCompanysPhone();

  Id batchId = Database.executeBatch(updcon);

Use of salesforce batch apex class with a real time scenario

Use of salesforce batch apex class with a real time scenario

Use of salesforce batch apex class with a real time scenario

You can also schedule the above class using a scheduler class to run daily.

Scheduling apex job:

To Schedule the batch class, you need a class which implements schedulable interface,

global class schedulebatch implements schedulable{

   global void execute(Schedulablecontext sc)

          {

             UpdateContactCompanysPhone acc=new UpdateContactCompanysPhone ();

             //Database.executeBatch(instance,size);

             Database.executeBatch(acc,200);

          }

}

Now, you can schedule class schedulebatch from User Interface,

Setup>develop>apex classes> schedule apex.

Best Practices:

Only use Batch Apex if you have more than one batch of records. If you don't have enough records to run more than one batch, you are probably better off using Queueable Apex.

Tune any SOQL query to gather the records to execute as quickly as possible.

Minimize the number of asynchronous requests created to minimize the chance of delays.

Use extreme care if you are planning to invoke a batch job from a trigger. You must be able to guarantee that the trigger won’t add more batch jobs than the limit.