개발 환경
php 버전: 7.4
laravel 버전: 6.20.26
문제 상황
현재 회사에서 개발중인 이커머스 관련 솔루션에서 사용자 결제시에 사용자에게 결제 관련 메일을 보내주는 기능이 존재한다.
원래 존재했던 기능이지만, 결제 과정에서 smtp를 통한 이메일 전송시에 결제 트랜잭션 소모시간이 과도하게 길어지는 이슈가 있어, 이메일 전송 로직자체를 큐로 분리하기로 했다.
https://laravel.kr/docs/8.x/queues
라라벨 8.x - Queues-큐
라라벨 한글 메뉴얼 8.x - Queues-큐
laravel.kr
라라벨 큐란 특정 작업들을 비동기적으로 진행할수 있도록 하여, 백그라운드에서 처리될 수 있는 대기 작업을 쉽게 생성할 수 있도록 해주는 라라벨의 아주 좋은 기능이다.
회사 솔루션 특성상, 레디스를 아직 지원하지 않기 때문에, 데이터베이스 큐를 이용하여 개발하고 있었으나, 한가지 문제가 발생했다.
모든 Notification들이 그런것은 아니나, 특정 Notification 들을 큐를 이용해 전송할 때, 오류가 발생하는 이슈였다.
확인해보니, Fatal 에러로 메모리가 exhaust 되는 이슈였다.
정확히 이슈가 발생하는 지점은 작업을 라라벨 큐에서 사용할 수 있게 실질적인 작업정보들을 직렬화하여 저장하는데
바로 이 `직렬화` 할 때, 메모리 과부화 현상이 발생하는 것이었다.
코드 상에서는 위 코드에서
$command = $this->jobShouldBeEncrypted($job) && $this->container->bound(Encrypter::class)
? $this->container[Encrypter::class]->encrypt(serialize(clone $job))
: serialize(clone $job);
부분에서 serialize(clone $job); 을 통해, job을 serialize하는 과정에서 발생하는 이슈였다.
그렇다면, 메모리 과부하가 발생하는 이유는 무엇이었을까, 바로 Cyclic serialization 때문이었다.
예를 들어, 아래와 같은 테스트 케이스로 모델간의 관계를 만들었을 때 문제가 발생한다.
<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Queue\SerializesModels;
class A extends Model {
public function b()
{
return $this->belongsTo(B::class);
}
}
class B extends Model
{
public function a()
{
return $this->belongsTo(A::class);
}
}
$a = new A;
$b = new B;
$a->setRelation('b', $b);
$b->setRelation('a', $a);
$serialized = serialize(new class ($a) {
use SerializesModels;
public A $a;
public function __construct(A $a)
{
$this->a = $a;
}
});
A에서도 B에 대한 관계를 가지고 있고, B에서도 A에 대한 관계를 가지게 되어 각 object에 relation property가 순환적으로 생산되어, serialization 시에 순환적으로 계속 직렬화 작업이 이루어지는 것이다..!
해결
https://abedt.com/blog/laravel-circular-relation-infinite-loop/
https://github.com/laravel/framework/issues/23505
[5.6] Cyclic serialization of relationships · Issue #23505 · laravel/framework
Laravel Version: 5.6.11 PHP Version: 7.1 Database Driver & Version: MySQL 5.7 Description: A new SerializesModels helper causes cyclic serialization of relations. Steps To Reproduce: There is the f...
github.com
Laravel serialization circular relation
Fix the issue where the serialized object in the queue contains infinite circular relations, causing the job to timeout and fail
abedt.com
해당 이슈가 이전에도 발생했고, 생각보다 관련 사례가 많아서 위 링크들을 참고하여, 문제가 되는 relation들에 대해 모델의 setRelations 메소드를 이용하여 최적화를 진행해주었고, 그 결과 정상적으로 작동하는 것을 확인할 수 있었다.
'개발 > 라라벨' 카테고리의 다른 글
eloquent 모델에 keyType 지정하지 않을 시, relation에 접근할 수 없는 이슈 (1) | 2023.10.17 |
---|---|
tap, tap, tap (0) | 2023.09.27 |
laravel Request life-cycle (생명주기) dd로 따라가보기 (2) (0) | 2023.09.11 |
laravel Request life-cycle (생명주기) dd로 따라가보기 (1) (0) | 2023.09.05 |
Eloquent Collection과 Support Collection 은 뭐가 다를까? (0) | 2023.09.01 |