[세일즈포스 PD1] Asynchronous Apex 비동기 Apex (Future, Batch, Queueable, Scheduler)

랸나·2024년 7월 30일
0

세일즈포스

목록 보기
4/4
post-thumbnail

Asynchronous Processing Basics (비동기 처리 기본)

  • 사용자가 작업이 완료될 때 까지 기다릴 필요 없이 '백그라운드' 에서 작업을 실행하는 프로세스나 함수
  • 사용자 효율성 , 더 높은 거버너 리밋 (비동기 쿼리 200/동기 쿼리100), 확장성

APEX 비동기 처리의 종류

타입내용용례
Future 메소드자체 스레드에서 실행, 리소스를 사용할 수 있을 때가지 시작되지 않음웹 서비스 콜아웃
Batch Apex일반적인 거버너 리밋을 넘는 대규모 작업 수행데이터 삭제, 레코드 아카이빙
Queueable Apex퓨처 메소드와 유사하나, 추가적인 체이닝을 제공하고 더 복잡한 데이터 유형 사용 가능웹 서비스에서 순서가 있는 작업이 필요할 때
Scheduled Apex지정된 시간에 APEX 실행 예약매일,주간으로 반복되는 업무

비동기 처리 작업 순서

  1. Enqueue
    요청이 큐로 적재됨. 해당 요청을 처리하기 위해 적절한 데이터와 함께 요청을 대기열에 넣는다.

  2. Persistence
    대기열에 있는 요청은 지속되며, 장애 복구를 위해 영구 저장소에 저장되고 트랜잭션을 제공

  3. Dequeue
    요청을 마치면 요청은 큐에서 제거되고, 처리가 실패하면 트랜잭션이 요청이 손실되지 않도록 보장한다.

자원 변환

  • 비동기 처리는 api 실시간 상호작용보다 우선순위가 낮음.
    ==> 따라서, 큐잉 프레임워크는 서버 메모리 및 CPU 사용과 같은 시스템 리소스 모니터링함.
    ==> 만약 임계값을 초과하면 비동기 처리를 줄인다.
    ==> 멀티테넌트가 스스로 시스템을 보호하는 것

🖥️Future Method (퓨처 메소드)

@future메소드가 사용되는 경우

  • 외부 웹 서비스에 대한 콜아웃: 트리거에서 콜아웃을 하거나 DML 작업을 수행한 후 콜아웃을 하는 경우 future 또는 queueable 메서드를 사용해야 함.
    트리거의 콜아웃은 콜아웃의 수명 동안 데이터베이스 연결을 열어두며 이는 멀티테넌트 환경에서 금지됨.

  • 별도의 스레드에서 실행하려는 작업 : 여기에는 리소스를 많이 소모하는 계산이나 레코드 처리 등이 포함

  • 혼합 DML 오류를 방지하기 위해 다른 sObject 유형에서 DML 작업을 분리: 자세한 내용은 DML 작업에서 함께 사용할 수 없는 sObject를 참조 (예외 경우임)

규칙

  • static 메소드여야 한다.
  • void 유형만 반환한다.
  • 매개변수는 기본 데이터 유형, 기본 데이터 유형 배열, 기본 데이터 유형 컬렉션이여야 한다. -- 표준/사용자 정의 개체를 파라미터로 사용 불가 ==> 비동기가 실행되는 중간에 개체가 변경될 수 있기 때문!
  • 보통은 레코드 ID 리스트를 전달해서 사용
  • 호출된 순서대로 실행된다는 보장이 없음 => 두 메서드가 같은 레코드를 업데이트하면 락이 걸리거나, 런타임 에러 발생할 수 있음.
  • 퓨처 메소드에서 퓨처 메소드를 사용할 수 없다.
public class SomeClass {
  @future
  public static void someFutureMethod(List<Id> recordIds) {
    List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds];
    // process account records to do awesome stuff
  }
}
  • 테스트 클래스 : 테스트코드는 실제로 외부 시스템에 콜아웃을 보낼 수 없으므로, 콜아웃을 mock으로 만들어야한다.
//가상 mock 콜아웃 응답기
@isTest
public class SMSCalloutMock implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest req) {
        // Create a fake response
        HttpResponse res = new HttpResponse();
        res.setHeader('Content-Type', 'application/json');
        res.setBody('{"status":"success"}');
        res.setStatusCode(200);
        return res;
    }
}
//
@IsTest
private class Test_SMSUtils {
  @IsTest
  private static void testSendSms() {
    Test.setMock(HttpCalloutMock.class, new SMSCalloutMock());
    Test.startTest();
      SMSUtils.sendSMSAsync('111', '222', 'Greetings!');
    Test.stopTest();
    // runs callout and check results
    List<SMS_Log__c> logs = [select msg__c from SMS_Log__c];
    System.assertEquals(1, logs.size());
    System.assertEquals('success', logs[0].msg__c);
  }
}

사용 Tips

  • 같은 퓨처 메소드에서 모든 콜아웃을 묶어 사용하면 좋음.
  • 많은 수의 레코드를 처리한다면 배치 사용

🖥️Batch Apex(배치 Apex)

배치 apex가 사용되는 경우

  • 수천, 수백만개의 데이터를 삭제하고 저장하는 경우.

배치 내부 동작 방식

  • 배치 클래스 실행 로직은 레코드 배치마다 1번씩 호출됨.
  • 작업이 apex 작업 대기열에 배치되고, 개별 트랜잭션으로 실행됨.
    => 장점1. 트랜잭션별로 거버너 리밋이 적용됨!
    => 장점2. 한 배치가 성공적으로 처리되지 않아도 다른 모든 배치 트랜잭션은 롤백되지 않음.

배치 클래스 구현 방법

  1. Database.Batchable 인터페이스를 구현해야함
  2. start / execute / finish 메소드를 오버라이딩 재정의 해야함.
  • start
    - execute에 넘길 레코드나 개체를 수집하는 메소드.
    - 배치가 시작될 때 처음 한번 호출된다.
    • Database.QueryLocator 오브젝트를 반환하거나, 잡에 넘길 레코드나 개체를 포함한Iterable을 반환
    • QueryLocator : 간단한 SOQL (거버너 리밋 우회, 최대 5000만개의 레코드 쿼리 가능), 만약 복잡한 구문을 처리해야한다면 커스텀 Iterable을 사용하기
  • execute
    - 실제 작업을 수행하는 메소드
    • 기본 배치 사이즈 : 200
    • 레코드들에 대한 작업의 순서를 보장하지는 않는다.
    • Database.BatchableContext를 참조
  • finish
    - 모든 배치가 처리된 후 한 번 호출됨.
public class MyBatchClass implements Database.Batchable<sObject> {
    public (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {
        // collect the batches of records or objects to be passed to execute
    }
    public void execute(Database.BatchableContext bc, List<P> records){
        // process each batch of records
    }
    public void finish(Database.BatchableContext bc){
        // execute any post-processing operations
    }
}

//이후 호출해서 배치 수행.
MyBatchClass myBatchObject = new MyBatchClass();
Id batchId = Database.executeBatch(myBatchObject, 100);


//이는 Job Queue에서 관리 가능
AsyncApexJob job = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID = :batchId ];

사용 Tips

  • Database.Stateful 을 클래스에서 정의하면 트랜잭션간 작업간 상태를 유지할 수 있음.
  • 레코드 배치가 2개 이상 있는 경우에 사용. 두개 이상이 아닌 경우는 Queueable이 더 나음
  • 트리거에서 배치를 사용한다면 거버너 리밋을 초과하지 않도록 보장해야함.
  • 지연을 막기 위해, 비동기 요청의 수를 최소화해야함.

🖥️Queueable Apex (큐어블 Apex)

Queueable apex가 사용되는 경우

  • future 메소드 + Batch의 장점
  • sObject나 커스텀 Apex Type같은 non-primitive types 를 사용할 수 있다.
  • System.enqueueJob()을 통해 작업을 식별하고 상황을 모니터링
  • 작업 체이닝이 필요할 때
public class UpdateParentAccount implements Queueable {
    private List<Account> accounts;
    private ID parent;
    public UpdateParentAccount(List<Account> records, ID id) {
        this.accounts = records;
        this.parent = id;
    }
    public void execute(QueueableContext context) {
        for (Account account : accounts) {
          account.parentId = parent;
          // perform other processing or callout
        }
        update accounts;
    }
}

// find all accounts in ‘NY’
List<Account> accounts = [select id from account where billingstate = ‘NY’];
// find a specific parent account for all records
Id parentId = [select id from account where name = 'ACME Corp'][0].Id;
// instantiate a new instance of the Queueable class
UpdateParentAccount updateJob = new UpdateParentAccount(accounts, parentId);
// enqueue the job for processing
ID jobID = System.enqueueJob(updateJob);

작업 체이닝

public class FirstJob implements Queueable {
    public void execute(QueueableContext context) {
        // Awesome processing logic here
        // Chain this job to next job by submitting the next job
        System.enqueueJob(new SecondJob());
    }
}
  • 작업 체이닝은 부모 작업에 대해서 자식 작업은 1개만 존재 가능 (여러개의 자식 작업은 금지)
  • 체인의 깊이에는 제한이 없음. (디벨로퍼 에디션 / 트라이얼 => 4번의 체이닝만 가능)

🖥️Schedule Job Apex Scheduler(스케줄 잡 스케줄러)

Scheduler apex가 사용되는 경우

  • 일일 또는 주간 유지 관리 작업 (반복되는 업무)

스케줄러 구현 방법

  1. Schedulable 인터페이스를 구현
  2. execute 메서드를 오버라이딩 재정의
  3. System.schedule()을 통해 스케줄을 예약한다.
public class SomeClass implements Schedulable {
    public void execute(SchedulableContext ctx) {
        // awesome code here
    }
}
RemindOpptyOwners reminder = new RemindOpptyOwners();
// Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
String sch = '20 30 8 10 2 ?';
String jobID = System.schedule('Remind Opp Owners', sch, reminder);

Tips

  • Apex 예약 작업은 100개만 가능.
  • 트리거 내에서 스케줄러를 정의한다면 극도로 조심해야함
  • 스케줄러에서는 웹서비스 콜아웃이 지원되지 않음 ==> future메소드를 활용하여 비동기 콜아웃을 만든 뒤, 이를 호출해줘야함.

출처 : https://trailhead.salesforce.com/ko/content/learn/modules/asynchronous_apex/async_apex_introduction?trailmix_creator_id=strailhead&trailmix_slug=prepare-for-your-salesforce-platform-developer-i-credential

profile
백엔드개발자

0개의 댓글