Remove the transient
keyword. It causes the variable not to get serialized.
Use the transient keyword to declare instance variables that can't be saved, and shouldn't be transmitted as part of the view state for a Visualforce page. For example:
Transient Integer currentTotal;
You can also use the transient keyword in Apex classes that are serializable, namely in controllers, controller extensions, or classes that implement the Batchable or Schedulable interface. In addition, you can use transient in classes that define the types of fields declared in the serializable classes.
Problem is...you can't serialize the request and response classes. You might find something like the below helpful:
Serializable Request
public class GenericRequest
{
String requestType;
String requestMethod;
String requestURI;
String requestParams;
String requestHeaders;
String requestBody;
public GenericRequest(RestRequest request)
{
this.requestType = 'Inbound';
this.requestMethod = request.httpMethod;
this.requestURI = request.requestURI;
this.requestParams = (request.params != null) ? JSON.serialize(request.params) : null;
this.requestHeaders = (request.headers != null) ? JSON.serialize(request.headers) : null;
this.requestBody = (request.requestBody != null) ? request.requestBody.toString() : null;
}
public GenericRequest(HttpRequest request)
{
this.requestMethod = request.getMethod();
this.requestURI = request.getEndpoint();
this.requestBody = request.getBody();
this.requestType = 'Outbound';
}
}
Serializable Response
public class GenericResponse
{
String responseBody;
Integer responseStatus;
public GenericResponse(RestResponse response)
{
if (response == null) return;
responseStatus = response.statusCode;
if (response.responseBody != null)
responseBody = response.responseBody.toString();
}
public GenericResponse(HttpResponse response)
{
if (response == null) return;
responseStatus = response.getStatusCode();
responseBody = response.getBody();
}
}
Queueable Constructor
final GenericRequest request;
public MyQueueable(HttpRequest input)
{
request = new GenericRequest(input);
}
Yes, queueable methods can run in parallel. As a proof of concept, here's some code that I wrote:
public class TenSecondQueueable implements Queueable {
public void execute(QueueableContext context) {
System.debug(LoggingLevel.ERROR, DateTime.now());
Long start = DateTime.now().getTime();
while(DateTime.now().getTime()-start<10000);
System.debug(LoggingLevel.ERROR, DateTime.now());
}
}
This method will run for approximately 10 seconds. I then ran this execute anonymous script:
System.enqueueJob(new TenSecondQueueable());
System.enqueueJob(new TenSecondQueueable());
The resulting output for both scripts was as follows:
>>>> JOB 1 <<<<
19:56:22.0 (4570498)|USER_DEBUG|[3]|ERROR|2017-03-22 00:56:22
19:56:32.4 (10004427521)|USER_DEBUG|[6]|ERROR|2017-03-22 00:56:32
>>>> JOB 2 <<<<
19:56:22.0 (2618567)|USER_DEBUG|[3]|ERROR|2017-03-22 00:56:22
19:56:32.1 (10001759396)|USER_DEBUG|[6]|ERROR|2017-03-22 00:56:32
As you can see, they both started and ended at the exact same time. Mind you, there's also no guarantee this will always happen. However, it's safe to say that it certainly can and will happen.
Odds are, you will probably want to use a locking algorithm to prevent this from happening. I think you'll probably want to add a sentry object (a record you can use to hold a mutex lock), so that the each additional action will need to wait. For example, you might add this near the beginning of your code:
insert new Mutex__c();
Mutex__c[] lock;
while(lock == null) {
try {
lock = [SELECT Id FROM Mutex__c LIMIT 1 FOR UPDATE];
} catch(QueryException e) {
}
}
As well as clean up near the end of your code:
delete lock;
What will happen here is that if the record you inserted was the oldest lock, your code will have the record lock and proceed immediately. If not, it'll wait idly by until the prior Queueable has finished doing what it needs to do. This should prevent any race conditions from Queueables that run close together, although this also essentially prevents more than one queueable running at a time, even if they wouldn't touch the same record(s).
You might also simply be able to get away with using a FOR UPDATE locking statement on your dynamically generated query. This will "probably" be safe enough for most purposes, but you'll need to do some testing.
Best Answer
If your class specifies "with sharing", it runs in user context. Otherwise, it should run in system context. You may want to use "without sharing" to make sure it runs in system context.