Как предотвратить одновременную обработку определенного задания в laravel?Php

Кемеровские программисты php общаются здесь
Ответить
Anonymous
 Как предотвратить одновременную обработку определенного задания в laravel?

Сообщение Anonymous »

В настоящее время я внедряю систему пополнения кошелька для приложения. Чтобы справиться с этим, я использую задание под названием AdjustWalletBalance. Задача просто считывает текущий баланс кошелька клиента и увеличивает его на определенную сумму. Ниже приведен код задания:

Код: Выделить всё

use App\Events\Tenant\WalletTopupSuccessful;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;

class AdjustWalletBalance implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* Create a new job instance.
*/
public function __construct(
/**
* The amount to add to wallet balance. Can be negative for deductions.
*/
public float $amount,
/**
* User who's wallet should be adjusted.
*/
public User $user,
)
{
$this->onQueue('wallets');
}

/**
* Execute the job.
*/
public function handle(): void
{
$process_id = mt_rand();
$wallet = $user->wallet;
\Log::debug("starting $process_id at " . now());
\Log::debug("adjusting wallet balance with amount: {$this->amount}");
\Log::debug("current balance for $process_id: {$wallet->balance}");

// Increasing the running time for the job to make it easier to have
// two simultaneous jobs running for the sake of this demonstration.
sleep(10);

$wallet->balance = $wallet->balance + $this->amount;
$wallet->save();
\Log::debug("$process_id updated balance to {$wallet->balance}");
}

public function middleware()
{
return [(new WithoutOverlapping($this->user->id))];
}

}

Поскольку обработка баланса кошелька пользователя является очень деликатной задачей, я не хочу, чтобы для конкретного пользователя выполнялось несколько экземпляров задания, что приводило бы к гонкам и неправильному обновлению баланса. , поэтому я добавил к заданию промежуточное программное обеспечение WithoutOverlapping, используя идентификатор пользователя в качестве уникального ключа. Но это не работает, я все равно могу выполнить задание параллельно для того же пользователя, хотя я добавил промежуточное программное обеспечение.
У меня есть такая запись маршрута:

Код: Выделить всё

Route::get('/queue-test', function() {
dispatch(new \App\Jobs\Tenant\AdjustWalletBalance(50, User::first());
});
Я запускаю два обработчика очереди одновременно, и если я посещаю маршрут дважды, я сразу вижу, что каждый обработчик начинает обрабатывать задание AdjustWalletBalance, чего не должно быть:
Изображение
. И когда я проверяю свои файлы журналов, чтобы дополнительно проверить последовательность выполнения заданий, я вижу это:

Код: Выделить всё

[2023-09-24 19:12:42] local.DEBUG: starting 1007152283 at: 2023-09-24 19:12:42
[2023-09-24 19:12:42] local.DEBUG: adjusting wallet balance with amount: 50
[2023-09-24 19:12:42] local.DEBUG: current balance for 1007152283: 0
[2023-09-24 19:12:43] local.DEBUG: starting 241490440 at: 2023-09-24 19:12:43
[2023-09-24 19:12:43] local.DEBUG: adjusting wallet balance with amount: 50
[2023-09-24 19:12:43] local.DEBUG: current balance for 241490440: 0
[2023-09-24 19:12:52] local.DEBUG: 1007152283 updated balance to 50
[2023-09-24 19:12:53] local.DEBUG: 241490440 updated balance to 50

Как вы уже могли заметить, это ОГРОМНАЯ проблема. На кошелек пользователя дважды был зачислен #50, но поскольку эти два задания выполняются одновременно, баланс кошелька пользователя обновляется до #50 вместо #100!
Я уже установил CACHE_DRIVER в массив, поэтому я не думаю, что это проблема с конфигурацией.
Я также пробовал использовать контракт MustBeUnique и устанавливать идентификатор пользователя как уникальный идентификатор, например

Код: Выделить всё

class AdjustWalletBalance implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(/**/)
{ /* Code */ }

public function uniqueId(): int
{
return $this->user->id;
}

public function handle()
{ /* Code */ }
}
Но я все равно получаю те же результаты.
Если я убью другого работника очереди и оставлю работать только одного, то последующие экземпляры задания добавляются в очередь, и кошелек пользователя корректно обновляется. Но у приложения будет несколько сотен пользователей, и мне нужно, чтобы несколько обработчиков очереди работали одновременно, чтобы быстро обрабатывать обновления их кошелька, поэтому один обработчик очереди не является вариантом.

Подробнее здесь: https://stackoverflow.com/questions/771 ... in-laravel
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Php»