Время ожидания функции контроллера Laravel истекло, 50000 пользователей

#php #laravel #eloquent

#php #laravel #красноречивый

Вопрос:

В моем контроллере у меня есть функция для проверки статуса подписки и обновления ее в моей базе данных. Проблема в том, что для 50000 пользователей требуется слишком много времени для завершения и тайм-аута.

 public function UpdateStatus(){
    $users = User::all();

    foreach($users as $user){
        $user->createOrGetStripeCustomer();
        $stripeSubs = $user->asStripeCustomer()->subscriptions->all();
        $dbSubs = DB::table('subscriptions')->select('stripe_id')->where('user_id', $user->id)->get();
        foreach($dbSubs as $check){
            $canDelete=0;
            foreach($stripeSubs as $value){
                if($check->stripe_id == $value->id){
                    $canDelete  ;
                }
            }


            if($canDelete==0){
                DB::table('subscriptions')->where('user_id', $user->id)->where('stripe_id',$check->stripe_id)->update(['stripe_status'=>'ended']);
            }
        }
        
    }
    
    return redirect('/dashboard');
}  
  

Я уверен, что мне даже не следует обрабатывать столько одновременно, но я как бы застрял здесь и не уверен, как именно подойти к этому. Моя цель — заставить это работать и оптимизировать его.

Комментарии:

1. как насчет того, чтобы сделать это заданием очереди для запуска в фоновом режиме

2. Может быть, chunk() может помочь?

Ответ №1:

вы используете job таким образом:

 php artisan make:job checkSubscrption
  

в вашем env-файле измените Queue_CONNECTiON=database ,
запустите php artisan queue:table и php artisan migrate
в вашем файле checkSubscription установите, что

 public function handle(){
            $users = User::all();
  
foreach($users as $user){
    $user->createOrGetStripeCustomer();
    $stripeSubs = $user->asStripeCustomer()->subscriptions->all();
    $dbSubs = DB::table('subscriptions')->select('stripe_id')->where('user_id', $user->id)->get();
    foreach($dbSubs as $check){
        $canDelete=0;
        foreach($stripeSubs as $value){
            if($check->stripe_id == $value->id){
                $canDelete  ;
            }
        }


        if($canDelete==0){
            DB::table('subscriptions')->where('user_id', $user->id)->where('stripe_id',$check->stripe_id)->update(['stripe_status'=>'ended']);
        }
    }
    
}}
  

в вашем контроллере:

 public function UpdateStatus(){

 checkSubscrption::dispatch();//you can use chunk method and pass your $users as params
 return redirect('/dashboard');}
  

затем запустите php artisan queue:work

Ответ №2:

Во-первых, вы должны сделать это в очередном задании: https://laravel.com/docs/8.x/queues
Таким образом, время ожидания скрипта не истечет.

Однако загрузка 50 тыс. красноречивых объектов в память не является оптимальной. Чтобы решить эту проблему, вы можете использовать отложенные коллекции: https://laravel.com/docs/8.x/collections#lazy-collections

 $users = User::cursor();

foreach ($users as $user) {
  // do stuff
}
  

Это выполнит только один запрос, но сохранит в памяти только один User объект.