2

Problem statement: External system is creating a custom object record and placing an attachment to the same record via composite REST API. After Insert trigger code that runs immediately for further processing the record is unable to fetch the attachments using SOQL at times (the behavior is inconsistent i.e., sometime the SOQL return the attachment, while other times it doesn't).

Question: Is the availability to attachment object records to SOQL query to be assumed as asynchronous in nature? (i.e. SOQL query needs to be run with 1 or 2 seconds of delay)

Let's assume that the external system is executing the following composite request:

{
    "compositeRequest": [
        {
            "method": "POST",
            "url": "/services/data/v52.0/sobjects/MyCustomObject__c",
            "referenceId": "myRef1",
            "body": {
                "Field1__c": "Value1"
            }
        },
        {
            "method": "POST",
            "url": "/services/data/v52.0/sobjects/Attachment",
            "referenceId": "myRef2",
            "body": {
                "ParentId": "@{myRef1.id}",
                "Body": "<base64 content here>",
                "Name": "MyFile.json"
            }
        }
    ]
}

Apex After Insert trigger code that follows immediately would execute something as shown below:

Set<Id> idSet = new Set<Id>();
for(MyCustomObject__c obj: Trigger.new){
    idSet.add(obj.Id);
}

List<Attachment> files = [SELECT id, Body FROM Attachment WHERE ParentId IN :idSet]);

The SOQL query in the above apex code doesn't fetch the attachments consistently. Sometimes, it returns nothing. I tried to execute this piece of code in async mode via future call, but I was able to observe the same behavior there as well.

I tried to mimic a delay of 1500ms in the apex code and then observed that the SOQL returns results consistently. I couldn't find any official documentation to support my doubts about attachments availability to be queried being asynchronous in nature.

Has anybody experienced similar problem? I'm not ruling out the possibility of Salesforce infrastructure problem or some problem to do with latest release.

2 Answers 2

1

There is nothing in the documentation to suggest composite requests are transactional. See related

Since composite graphs can have partial success, this makes sense.

One should not design the Apex side of things assuming some "predicable composite packaging" because the caller can adjust the "packaging" at will without your code ever being the wiser. If the caller was not using composite REST, two separate API calls would be made and you would know these were two separate transactions.

A better design would be for the parent object to be inserted with an expected count of attachments, and then a trigger that runs on Attachment which does work only when the attachment count equals the expected count.

6
  • I'm not sure if we can consider composite requests as non-transactional based on its behavior due to allOrNone flag, since this flag only determines the rollback strategy and not the transaction boundary as such.
    – arut
    May 12, 2022 at 9:47
  • We are in a closed environment where the caller has agreed upon the payload as well as composite structure with us, hence I wouldn't consider our code strictly falling under "predictable composite packaging". I had thought about the approach of designing this based trigger over Attachment object, but it will not work in my case because the problem that I'm trying to solve it for few integration use cases, and we do have attachment object being used for non-integration use cases as well. We might end up executing the trigger for a lot many cases where it really won't process anything.
    – arut
    May 12, 2022 at 9:54
  • 1
    having a trigger execute without doing anything is a normal pattern; also - you r response to sfdcfox indicates that you're dealing with an async pattern so I'm not sure what choice you have other than to design the workflow based on attachment insertion unless you want to go the scheduled job route
    – cropredy
    May 12, 2022 at 17:30
  • I've tried this code in both sync and async pattern; both have the same behavior (although the occurrence of the problem in async was slightly lesser). I'm dealing with a very large org here (where multiple LOB operate), so having a trigger execute without doing anything might be problematic in my case. We are trying to squeeze out every possible ounce of optimization as much as possible. But appreciate the response.
    – arut
    May 13, 2022 at 5:25
  • BTW, While testing out composite requests as a part of this troubleshooting, I was able to observe that composite API are not transactional (as you had pointed out to the link in your answer).
    – arut
    May 13, 2022 at 11:01
0

I don't have time to test this, but one obvious solution would be to use a record locking statement:

List<Attachment> files = [SELECT id, Body FROM Attachment WHERE ParentId IN :idSet FOR UPDATE];

This should guarantee that the content will be available.

3
  • This didn't help either. I'm not sure how this would help solve the problem though. The SOQL query would need to return something for the FOR UPDATE to work, isn't it? In my case, the SOQL query is unable to fetch the data.
    – arut
    May 12, 2022 at 11:27
  • 1
    @arut FOR UPDATE waits for any records that are in the process of being created or updated to be released from the previous transaction. I presumed that should have worked.
    – sfdcfox
    May 12, 2022 at 19:10
  • Thanks for the response. If I find anything, I'll post it here.
    – arut
    May 13, 2022 at 5:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .