Sorry for the long post. But an interesting finding with data points.
I did some testing around this. Here are my findings.
FIRST
services/data/v52.0/composite
{
"allOrNone":true,
"compositeRequest":[
{
"method":"PATCH",
"url":"/services/data/v48.0/sobjects/User/Username/***@***.com",
"referenceId":"updateMentorAvailabilityStatus",
"body":{
"CustomField__c":"true"
}
}
]
}
If we use Composite API and triggers are doing some heavy custom logic which takes more than 10 seconds.
trigger UserTrigger on User (before insert, before update, after insert, after update) {
Long startTime = System.currentTimeMillis();
for (Integer i = 0; i < 30000000; i++) {
}
Long endTime = System.currentTimeMillis();
System.debug('Total Time Taken -> ' + String.valueOf(endTime-startTime));
}
Then before hitting Concurrent API Request Limits (20 seconds limit for each call)
we will start getting Apex CPU Time Limit
{
"compositeResponse": [
{
"body": [
{
"message": "UserTrigger: System.LimitException: Apex CPU time limit exceeded",
"errorCode": "CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY",
"fields": []
}
],
"httpHeaders": {},
"httpStatusCode": 400,
"referenceId": "updateMentorAvailabilityStatus"
}
]
}
Which means that even though we have this limit of standard api for 20 seconds each, the triggers and apex classes running under this context still have 10 seconds apex CPU time limit.
SECOND
Tried reproducing REQUEST_LIMIT_EXCEEDED: ConcurrentRequests (Concurrent API Requests) Limit exceeded
. This comes when apex can run below 10 seconds. But there are more than required number threads accessing the apis at the same time. Other concurrent requests starts showing this exception.
Here I modified apex loop to run under the cpu time limit.
trigger UserTrigger on User (before insert, before update, after insert, after update) {
Long startTime = System.currentTimeMillis();
for (Integer i = 0; i < 20000000; i++) {
}
Long endTime = System.currentTimeMillis();
System.debug('Total Time Taken -> ' + String.valueOf(endTime-startTime));
}
And triggered 10 instances in parallel to user composite api.
And got this response .
Error: REQUEST_LIMIT_EXCEEDED: ConcurrentRequests (Concurrent API Requests) Limit exceeded.
THIRD
"Error performing apex: ConcurrentPerOrgLongTxn Limit exceeded.
This comes when I modified LongTxn class to increase the time in which the system returns the response.
So the scenario becomes. When a webservice call gets hit concurrently and the apex takes more than approx 8-9 seconds to return the response constantly. Then we receive this exception.
@RestResource(urlMapping='/LongTxn')
global with sharing class LongTxn {
@HttpGet
global static void handleGet() {
System.debug(LoggingLevel.ERROR, 'HandleGet called');
RestResponse res = RestContext.response;
res.addHeader('Content-Type', 'application/json');
for (Integer i = 0; i < 22000000; i++) {
}
res.responseBody = Blob.valueOf('{}');
}
SOLUTION for Concurrent API
Instead of composite api, I used Platform event, and used the same replication manner to replicate 10 concurrent calls.
And wrote a trigger on PE to update user.
/**
* Created by nagendrasingh on 29/05/22.
*/
trigger UserPETrigger on UserEvent__e (after insert) {
List<UserEvent__e> userEvents = Trigger.new;
List<User> lstUsers = new List<User>();
for (Integer i = 0; i < userEvents.size(); i++) {
lstUsers.add(new User(Username = userEvents.get(i).Username__c, MKApp_Availability_Status__c = userEvents.get(i).MKApp_Availability_Status__c));
}
}
And this was not giving any exceptions such as Concurrent API Requests
as the response is always under 200 ms.