Apex assíncrono – Queueable – Parte 2

Dando continuidade a série de posts sobre Apex Assíncrono, hoje falaremos sobre as classes Queueable, podemos dizer que as Queueable são uma evolução dos métodos futuros, pois nos permite ter controle da fila de execução. Se você não leu o post sobre métodos futuros, sugiro que de uma pausa nessa leitura, e de uma olhada nesse post.

O que são classes Queueable?

Podemos dizer que as classes Queueable são uma evolução dos métodos futuros, pois além de nos permitir realizar a execução de trabalhos do Apex assíncronos, também nos da um limite maior de governança da sua org, permitindo por exemplo a execução de mais SOQL dentro de uma mesma execução de processo do Apex, além de um limite de heap maior, e assim como os métodos futuros, as classes Queueable são executadas assim que a plataforma Salesforce considera que existem recursos livres para execução.

Quando você adiciona uma classe Queueable a fila de execução, você recebe um ID único, isso nos permite obter um controle maior da fila de processos, permitindo obter o status do processo na fila, esses dados ficam no objeto AsyncApexJob, e com base no Id que recebemos podemos fazer uma consulta SOQL para obter mais informações, veremos isso mais a frente.

Quando usar uma classe Queueable?

  • Para iniciar uma operação de longa duração e obter uma ID de controle da sua fila de execução
  • Para passar tipos complexos para um trabalho
  • Para encadear os trabalhos de longa duração

Estrutura de uma classe Queueable

Uma classe Queueable tem uma estrutura interessante, porque ela obrigatoriamente deve implementar uma Interface, essa interface é composta de um único método, chamado Execute, esse método por sua vez, recebe um parâmetro, uma instancia da classe QueueableContext, que nos permite por exemplo, obter o Id do processo em execução.

public class AsyncExecutionExample implements Queueable {
    public void execute(QueueableContext context) {
        //Registra no logo o Id do processo em execução.
        System.debug(context.getJobId());      
    }
}

E para colocarmos uma classe na fila de execução e obter um ID, faremos assim.

ID jobID = System.enqueueJob(new AsyncExecutionExample());
System.debug(jobID);

Uma vez que a nossa classe esta na fila, e temos o Id do processo, podemos então realizar a consulta no objeto AsyncApexJob  para obter mais detalhes do processo, e fazemos isso realizando a consulta SOQL abaixo.

AsyncApexJob jobInfo = [SELECT Status, NumberOfErrors FROM AsyncApexJob WHERE Id=:jobID];

O resultado da nossa consulta, é um objeto AsyncApexJob e podemos incluir na nossa consulta os seguintes campos:

Campo Descrição
ApexClassID Id da classe do Apex
CompletedDate Data e hora quando o processo foi completado
ExtendedStatus Quando seu processo receber um erro, uma descrição curta do primeiro erro será apresentado aqui
JobItemsProcessed Numero de processo executados, ele não se aplica para as Queueable, somente para as Batches, que veremos no próximo post :)
JobType Podendo ser:
  • Future
  • SharingRecalculation
  • ScheduledApex
  • BatchApex
  • BatchApexWorker
  • TestRequest
  • TestWorker
  • ApexToken
  • Queueable
MethodName Nome do método apex que iniciou a execução
NumberOfErrors Número total de lotes com falha. Um lote é considerado transacional, portanto, quaisquer exceções não tratadas constituem uma falha completa do lote.
Status Podendo ser:
  • Holding1
  • Queued
  • Preparing
  • Processing
  • Aborted
  • Completed
  • Failed

1 Disponível apenas quando for uma Batch

TotalJobItems Número total de lotes processados. Cada lote contém um conjunto de registros, novamente um item que só é aplicado as batches.

Queueable encadeados

Um recurso muito interessantes das Queueable, é permitir que um processo, crie um novo processo, ficamos limitados a 2 processos encadeados, mas em contra partida, isso nos permite duplicar novamente os limites, pois em uma única chamada do Apex, poderíamos criar 2 processos agendados, ou seja, um processo pai só poderá ter 1 processo filho, e cada um teria os seus limites separados, eu mesmo já utilizei esse recurso para conseguir fazer 200 chamadas callout em uma única chamada do Apex, basicamente o que precisamos fazer é ao final da execução do nosso processo, agendar um novo processo.

O código de um Queueable encadeado ficaria assim

public class AsyncExecutionExample implements Queueable {
    public void execute(QueueableContext context) {
        //Registra no logo o Id do processo em execução.
        System.debug(context.getJobId());

        //Encadeia esse processo criando um novo processo filho
        if (!Test.isRunningTest())
            System.enqueueJob(new SecondJob());
    }
}

Dois pontos importantes aqui, primeiro que um processo filho, não pode ser executado em classes de testes, por isso coloquei ali uma checagem para somente enfileirar o segundo processo, caso não seja uma rotina de teste unitário.

Caso você esqueça de fazer a checagem se é um teste sendo executado para enfileirar o segundo Queueable você receberá a seguinte mensagem de erro ao executar o seu teste:

20:30:45:035 FATAL_ERROR System.AsyncException: Maximum stack depth has been reached.

Caso seu usuário esteja utilizando o idioma em Português, a mensagem de  erro será:

20:27:37:025 FATAL_ERROR System.AsyncException: A profundidade máxima da pilha foi atingida.

E o segundo ponto é, caso você precise fazer uma chamada callout dentro de uma classe Queueable, você precisará implementar também a interface Database.AllowsCallouts, fazendo isso sua classe ficaria assim:

public class AsyncExecutionExample implements Queueable, Database.AllowsCallouts {
    public void execute(QueueableContext context) {
        //Registra no logo o Id do processo em execução.
        System.debug(context.getJobId());
    }
}

Cobertura de testes de classes Queueable

Antes de falarmos da cobertura de testes de uma classe Queueable, um ponto importante, ao executar o System.enqueueJob para agendar uma classe, ela não retornará um ID quando estivermos executando um teste unitário, então tenha sempre isso em mente ao criar suas classes de test, o retorno desse método sempre será null.

A cobertura da classe de teste unitário de uma classe Queueable seja a mesma lógica dos métodos futuros, ou seja, temos que fazer o uso do Test.startTest() e Test.stopTest() para que ela seja executada assim que o stopTest for chamado, permitindo que seja possível consultar os dados para o nosso Assert.

@isTest
public class AsyncExecutionExampleTest {
    @isTest
    static void test1() {
        Test.startTest();        
        System.enqueueJob(new AsyncExecutionExample());
        Test.stopTest();

        Account acct = [SELECT Name,Phone FROM Account WHERE Name='Acme' LIMIT 1];
        System.assertNotEquals(null, acct);
    }
}

Nesse exemplo acima, consideramos que a nossa classe de teste AsyncExecutionExample realizou a inserção de uma conta com o nome Acne, e validamos essa inserção após chamar o método Test.stopTest();

Entendendo e respeitando os limites

Assim como os métodos futuros, as classes Queueable também tem algumas vantagens em termos de limites, são elas:

  • Permite até 200 Consultas SOQL
  • Permite um heap de até 12MB
  • Permite um tempo maior de CPU, 60.000 milissegundos
  • Você pode adicionar até 50 processos em uma fila usando o System.enqueue em uma única transação do Apex, para verificar quantos já estão na fila, faça uma chamada para o método Limits.getQueueableJobs().
  • Cada classe Queueable pai, pode encadear somente 1 classe Queueable filha.

 

Por fim, eu entendo como boas práticas o uso das classes Queueable em cenários onde o processo a ser executado, não precisa ser executado de imediato e você precisa de maiores limites da plataforma, como por exemplo executar mais consultas SOQL, mais uso de heap e CPU, ou seja, quando o processo a ser executado é realmente pesado, mas nem tanto a ponto de precisar de vários lotes de processos, para isso utilizamos as Batches, mas falaremos delas no próximo post.

 

Um forte abraço trailblazer e até o próximo post :)

 

Fernando Sousa

Senior Salesforce Developer

Bacharel em Sistemas da Informação pela Universidade de Taubaté (UNITAU) e MBA em Projeto de Aplicações para Dispositivos Móveis pelo IGTI – Instituto de Gestão em Tecnologia da Informação. 

Comecei a programar bem cedo, por volta de 10 anos de idade, de maneira auto-didata passei por várias linguagens.

Em 2015 me conectei a plataforma Salesforce pela primeira vez, para fazer una integração entre um Aplicativos Mobile em android e o Salesforce Platform. 

Atualmente com as certificações Salesforce Certified Platform Developer I, Salesforce Certified Platform App BuilderSalesforce Certified Platform Developer IISalesforce Administrator e Sharing and Visibility.

Acompanhe meu Trailhead aqui.

 

ApexApex AssíncronoAssíncronoQueueableSalesforce Developer