function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
gvgv 

Error when using @Future

I had to change an existing method so that it would execute asynchoronously. Here is the original method
 

public class WorkflowTickler { public static void TickleOpportunitySalesGoal(Opportunity_Sales_Goal__c[] opportunitySalesGoalArr) { DateTime currentTime = System.Now(); for(Opportunity_Sales_Goal__c opportunitySalesGoal:opportunitySalesGoalArr) { opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime; } update opportunitySalesGoalArr; }}

 

 

The trigger that calls the method is

 

 

trigger syncParentOpportunitySales on Sales_Goal__c (after update) { Opportunity_Sales_Goal__c[] lstOpportunitySalesGoal = [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]; WorkflowTickler.TickleOpportunitySalesGoal(lstOpportunitySalesGoal); }

 I ran into Too many rows issue when trying to update rows. So I am trying to use the @future to resolve the issue.

 

But when I try to use @future in the method it would not work since I keep getting this error

 

 

Save error: Unsupported parameter type LIST:SOBJECT:Service__c e

 

 The documentation says 

List, Array of SObject cannot be a parameter to an @future method.

How to solve this issue now. This is in production :(
 
Thanks
 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
mattdarnoldmattdarnold

Take a look at the following. Essentially, you need to use that set as a parameter for your method and then use it as a filter in a query using "in" and then generate a list of objects to update. I didn't test this, but it should be functional.

 

public class WorkflowTickler {

@future(callout = false)
public static void TickleOpportunitySalesGoal(Set<String> s) {
DateTime currentTime = System.Now();

List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>();
for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c where id in: s]) {
opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;
opportunitySalesGoalToUpdate.add(opportunitySalesGoal);
}
update opportunitySalesGoalToUpdate;
}

}

 

 

 

All Answers

David VPDavid VP

The docs also say :

 

"The parameters specified must be primitive dataypes, arrays of primitive datatypes, or collections of primitive datatypes"

 So what you can do is pass your Trigger.newMap.keySet() to your @future method and do your query there.

 

 

-David

gvgv

Hi David

 

Thanks for replying. But in teh @future method how would I receive the trigger.newmap.keyset()?

 

The trigger would pass it as

trigger syncParentOpportunitySales on Sales_Goal__c (after update) { WorkflowTickler.TickleOpportunitySalesGoal(Trigger.newMap.keyset()); }

But how would do the @future method recieve this without the list? or am I missing something very obvious

 

 

future (callout=true) public static void TickleOpportunitySalesGoal(List<Opportunity_Sales_Goal__c> OpportunitySalesGoalList) { } }

 

I don't know how else to call without List data type?

 

If I use list data type as a parameter in the @future, I still get the same error

 

Unsupported parameter type LIST:SOBJECT:Opportunity_Sales_Goal__c

mattdarnoldmattdarnold

In the @future method, you should set your parameter as a set of type String. So:

 

future (callout=true)
public static void TickleOpportunitySalesGoal(Set<String> s) {
// Then use s as a parameter for a query to get your SObjects
}

 

You can then use this set as a parameter for a query to get your SObjects and make the needed updates. If using newMap.keySet() doesn't work, try generating a set of type String with the IDs of the incoming records (not sure if newMap.KeySet() is an ID type).

 

 

gvgv

Thanks Matt for replying. 

 

But If  I use set<string> and try to loop through the trigger.new I get a 

 

Loop variable must be of type Id

 

error.The code is

 

 

Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c link:Trigger.newMap.keyset()){ opportunitysalesids.add(link.name); }

 


 

         

I have tried different variants of the above code but nothing works. Can you help?

 

 

David VPDavid VP

Just copy the data in the Set<ID> to a Set<String> using a for loop.

 

 

gvgv

Is there a way to copy values from a list to a set?

 

I have a list of objects  in a list that needs to be copied to a set of strings so I can pass it to the @futuire method

 

Opportunity_Sales_Goal__c[] lstOpportunitySalesGoal = [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()];

 

This is the list that needs to be copied to a set of string

 

Thanks

 

mattdarnoldmattdarnold

Yes. Do this:

 

Set<string> opportunitysalesids = new Set<string> ();

for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]) {
opportunitysalesids.add(osg.id);
}

// Call @future method with opportunitysalesids as a parameter

 

 

 

gvgv

Thanks very much Matt. I am not getting any error now. But the receiving method with the @future call out access the values from an array like this. 

 

public class WorkflowTickler {

  @future(callout = true)

  public static void TickleOpportunitySalesGoal(Opportunity_Sales_Goal__c[] opportunitySalesGoalArr) {

    

    DateTime currentTime = System.Now();

    for(Opportunity_Sales_Goal__c opportunitySalesGoal:opportunitySalesGoalArr) {

      opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;     

    }

    

    update opportunitySalesGoalArr;

  }

}

 

But if I pass the value as 'Set' how can the values be passed to the array?

 

Thanks again for patiently answering my queries

 

I appreciate  your help and time

mattdarnoldmattdarnold

Take a look at the following. Essentially, you need to use that set as a parameter for your method and then use it as a filter in a query using "in" and then generate a list of objects to update. I didn't test this, but it should be functional.

 

public class WorkflowTickler {

@future(callout = false)
public static void TickleOpportunitySalesGoal(Set<String> s) {
DateTime currentTime = System.Now();

List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>();
for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c where id in: s]) {
opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime;
opportunitySalesGoalToUpdate.add(opportunitySalesGoal);
}
update opportunitySalesGoalToUpdate;
}

}

 

 

 

This was selected as the best answer
gvgv
Thanks Matt very much for helping out. It does not give any compile errors and it seems to work without run time errors. Since the method is asynchoronous should I give some time for the values to be updated?
mattdarnoldmattdarnold

You should, but in my experience, it works pretty quickly - within 30 seconds typically (this can vary based on Salesforce utilization at the time). Instead, check the following to see if the job completed successfully or if there were errors:

 

Setup > Administration Setup > Monitoring > Apex Jobs

 

-- Matt

gvgv

Thanks Matt.

I could see the status on the apex jobs pretty quicly but if there are 3 jobs 2 is completed and 1 is failed. Is there a way to find out why it failed?

 

Also after the @future call the system is quite slow . Not sure if its my perception, but it has happened 3/4 times. I have to log out and then log back in then its normal . But whenever I access the object which calls the @future call, it seems to be slow.

 

Is there even a possibility that @future call can slow down the system?

 

THanks

 

 

 

Message Edited by gv on 07-20-2009 12:39 AM
Message Edited by gv on 07-20-2009 12:39 AM
mattdarnoldmattdarnold

You should try turning on the debug logs and then rerunning the process that calls your @future method and review the logs. How many records does this typically modify?

 

Since the @future method is asynchronous and runs from a trigger when the system has sufficient resources, you shouldn't be seeing any kind of performance change when accessing the objects modified in that method.

 

-- Matt

gvgv

Hello Matt and others

 

I implemented @future method to be called from my trigger and it seemed to be working fine. But in production I keep getting the following error messages

 

Failed to invoke future method 'public static void TickleOpportunitySalesGoal(SET:String)'

 

This is the method thats defined as @future callout = true

 

 I also get this error message from the trigger thats calling the method

 

syncParentOpportunitySales: execution of AfterUpdate

 

caused by: System.AsyncException: Future method cannot be called from a future method: TickleOpportunitySalesGoal(SET:String)

 

 

Can anyone shed more light on these errors  

 

Pls.let me know if you need more info 

 

 

 

 

mattdarnoldmattdarnold

Within TickleOpportunitySalesGoal are you calling another @future method? If so, this is what's causing your error as you can't call another @future method from your original @future call. If not, post your code and I'll take a look.

 

-- Matt

gvgv

Thanks Matt for replying. I am not calling @future inside another @future. I am posting my code so you can see if am ding something incorrect

This is the first trigger

trigger syncOpportunitySalesGoal on Opportunity (after update) { Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Opportunity__c in :Trigger.newMap.keyset()]) { opportunitysalesids.add(osg.id); } WorkflowTickler.TickleOpportunitySalesGoal(opportunitysalesids); }

 

This is the second trigger

 

trigger syncParentOpportunitySales on Sales_Goal__c (after update) { Set<string> opportunitysalesids = new Set<string> (); for (Opportunity_Sales_Goal__c osg : [Select Id from Opportunity_Sales_Goal__c where Sales_Goal__c in :Trigger.newMap.keyset()]) { opportunitysalesids.add(osg.id); } WorkflowTickler.TickleOpportunitySalesGoal(opportunitysalesids); }

 

 

This is the class with the @future method

public class WorkflowTickler { @future(callout = true) public static void TickleOpportunitySalesGoal(Set<String> s) { DateTime currentTime = System.Now(); List<Opportunity_Sales_Goal__c> opportunitySalesGoalToUpdate = new List<Opportunity_Sales_Goal__c>(); for(Opportunity_Sales_Goal__c opportunitySalesGoal : [select id, name, Last_Sync_To_Parent__c from Opportunity_Sales_Goal__c where id in: s]) { opportunitySalesGoal.Last_Sync_To_Parent__c = currentTime; opportunitySalesGoalToUpdate.add(opportunitySalesGoal); } update opportunitySalesGoalToUpdate; } }

 

mattdarnoldmattdarnold

I didn't see anything overly wrong with your code. Do you have any workflows or triggers that run on Opportunity_Sales_Goal__c that update Opportunity or Sales_Goal__c? (Trying to figure out if your updates in the @future method result in updates that result in additional @future methods.)

 

-- Matt

gvgv

Thanks Matt. I am looking at the workflows and validation rules right now. There are way too many. 

 

But is there a way to rewrie this code without using @future and also avoid too many rows error?

 

Thanks

 

gvgv
Matt and others
 
Can I use static variable to avoid the @future calling @future (to avoid recursion) , Can yo provide me with an example as to how would i implement it. 
 
I declard a static variable in the class and how do I use taht in the trigger.
pls.let me know 
Message Edited by gv on 08-12-2009 12:48 PM
mattdarnoldmattdarnold

It would be best to just remove whatever logic is causing @future methods to call other @future methods. But, if you just want to make sure your trigger doesn't call another @future method after calling the initial @future method you could do something like the following:

 

1. Add a true/false field to your object called FutureMethodCalled - defaults to false

2. In your trigger query for this and ensure that it is marked as false, if so add that object id to the set of IDs to pass to the @future method

3. Set the field to true on the objects passed to the @future method within your calling trigger

 

This should stop future calls, but it will also only allow you to call your @future method once with the specified records so I think it is probably best to work through your workflow rules / other triggers and modify those that are causing the issue initially.