Can Queueable solve "Future method cannot be called from a future or batch method"?
Yes, Queueable can be called from Database.Batchable classes. You must go through enqueueJob for this to work:
public class BatchClass implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext context) {
return Database.getQueryLocator([SELECT Id FROM Account]);
}
public void execute(Database.BatchableContext context, Account[] records) {
System.enqueueJob(new QueueClass(new Map<Id, Account>(records).keySet()));
}
public void finish(Database.BatchableContext context) {
}
}
public class QueueClass implements Queueable {
Set<Id> recordids;
public QueueClass(Set<Id> recordIds) {
this.recordIds = recordIds;
}
public void execute(QueueableContext context) {
// Do something here
}
}
Note that you're limited to just one enqueueJob call per execute in the Database.Batchable class. This is to prevent explosive execution (a so-called "Rabbit Virus" effect).
Wasted about 2 months to get an answer from salesforce support why I get
Too many queueable jobs added to the queue: 2
when documentation (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm) says that the limit is 50 for entry point?
The root cause was in the undocumented limit for invocation of System.enqueueJob
from batch context mentioned by @sfdcfox
In my case, to prevent hitting of this limit I checked the context in which my method executes, see code below:
if (FALSE == System.isBatch() || FALSE == System.isFuture()) {
System.enqueueJob(worker);
}
You can prevent this by checking the limit before enqueuing:
if(Limits.getQueueableJobs() < Limits.getLimitQueueableJobs()){
System.enqueueJob(worker);
}