laravel / lumen-framework

The Laravel Lumen Framework.
https://lumen.laravel.com
MIT License
1.47k stars 419 forks source link

SerializableClosure InvalidSignatureException being thrown when calling a batched job that uses Encrypter #1232

Closed MattApril closed 2 years ago

MattApril commented 2 years ago

Description:

I'm getting a Laravel\SerializableClosure\Exceptions\InvalidSignatureException error when running batches with callbacks (e.g. then()) where the job uses the Crypt facade (encrypter binding). However, this only seems to happen sometimes, but very often. Retrying the job in a new process (queue:work) usually works the second time around.

Steps To Reproduce:

  1. Create a job that uses Crypt facade
    
    use Illuminate\Bus\Batchable;
    use Illuminate\Support\Facades\Crypt;

class BatchableJob extends Job { use Batchable;

public function handle() {
    Crypt::encryptString('test');
    $this->release(); // so we can retry job as many times as necessary.
}

}


2. Queue the job as part of a batch, with a callback.

Bus::batch([new BatchableJob]) ->name('Test') ->allowFailures() ->then(function (){}) ->dispatch();


3. execute job: `php artisan queue:work --once`

**Expected result:** job is executed successfully and released back onto queue
**Actual result:** 

local.ERROR: Your serialized closure might have been modified or it's unsafe to be unserialized. {"exception":"[object] (Laravel\SerializableClosure\Exceptions\InvalidSignatureException(code: 0): Your serialized closure might have been modified or it's unsafe to be unserialized. at /home/vagrant/code/billing-api/vendor/laravel/serializable-closure/src/SerializableClosure.php:123) [stacktrace]

0 [internal function]: Laravel\SerializableClosure\SerializableClosure->__unserialize()

1 /home/vagrant/code/billing-api/vendor/illuminate/bus/DatabaseBatchRepository.php(322): unserialize()

2 /home/vagrant/code/billing-api/vendor/illuminate/bus/DatabaseBatchRepository.php(341): Illuminate\Bus\DatabaseBatchRepository->unserialize()

3 /home/vagrant/code/billing-api/vendor/illuminate/bus/DatabaseBatchRepository.php(85): Illuminate\Bus\DatabaseBatchRepository->toBatch()

4 /home/vagrant/code/billing-api/vendor/illuminate/bus/Batchable.php(24): Illuminate\Bus\DatabaseBatchRepository->find()

5 /home/vagrant/code/billing-api/vendor/illuminate/queue/CallQueuedHandler.php(281): App\Jobs\WarnAccountInArrears->batch()

6 /home/vagrant/code/billing-api/vendor/illuminate/queue/CallQueuedHandler.php(259): Illuminate\Queue\CallQueuedHandler->ensureFailedBatchJobIsRecorded()

7 /home/vagrant/code/billing-api/vendor/illuminate/queue/Jobs/Job.php(213): Illuminate\Queue\CallQueuedHandler->failed()

8 /home/vagrant/code/billing-api/vendor/illuminate/queue/Jobs/Job.php(192): Illuminate\Queue\Jobs\Job->failed()

9 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(581): Illuminate\Queue\Jobs\Job->fail()

10 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(527): Illuminate\Queue\Worker->failJob()

11 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(455): Illuminate\Queue\Worker->markJobAsFailedIfWillExceedMaxAttempts()

12 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(432): Illuminate\Queue\Worker->handleJobException()

13 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(378): Illuminate\Queue\Worker->process()

14 /home/vagrant/code/billing-api/vendor/illuminate/queue/Worker.php(329): Illuminate\Queue\Worker->runJob()

15 /home/vagrant/code/billing-api/vendor/illuminate/queue/Console/WorkCommand.php(128): Illuminate\Queue\Worker->runNextJob()

16 /home/vagrant/code/billing-api/vendor/illuminate/queue/Console/WorkCommand.php(112): Illuminate\Queue\Console\WorkCommand->runWorker()

17 /home/vagrant/code/billing-api/vendor/illuminate/container/BoundMethod.php(36): Illuminate\Queue\Console\WorkCommand->handle()

18 /home/vagrant/code/billing-api/vendor/illuminate/container/Util.php(41): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()

19 /home/vagrant/code/billing-api/vendor/illuminate/container/BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure()

20 /home/vagrant/code/billing-api/vendor/illuminate/container/BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod()

21 /home/vagrant/code/billing-api/vendor/illuminate/container/Container.php(653): Illuminate\Container\BoundMethod::call()

22 /home/vagrant/code/billing-api/vendor/illuminate/console/Command.php(171): Illuminate\Container\Container->call()

23 /home/vagrant/code/billing-api/vendor/symfony/console/Command/Command.php(291): Illuminate\Console\Command->execute()

24 /home/vagrant/code/billing-api/vendor/illuminate/console/Command.php(156): Symfony\Component\Console\Command\Command->run()

25 /home/vagrant/code/billing-api/vendor/symfony/console/Application.php(989): Illuminate\Console\Command->run()

26 /home/vagrant/code/billing-api/vendor/symfony/console/Application.php(299): Symfony\Component\Console\Application->doRunCommand()

27 /home/vagrant/code/billing-api/vendor/symfony/console/Application.php(171): Symfony\Component\Console\Application->doRun()

28 /home/vagrant/code/billing-api/vendor/illuminate/console/Application.php(102): Symfony\Component\Console\Application->run()

29 /home/vagrant/code/billing-api/vendor/laravel/lumen-framework/src/Console/Kernel.php(116): Illuminate\Console\Application->run()

30 /home/vagrant/code/billing-api/artisan(35): Laravel\Lumen\Console\Kernel->handle()

31 {main}

"}



### Debugging Notes:
- The problems is happening when unserializing the batch options.
- In SerializableClosure::__unserialize, $data['serializable'] is always an instance of Laravel\SerializableClosure\Serializers\Native. The exception happens when Signed::$signer is NOT null - this happens some of the time, the other times Signed::$signer is defined.
- Signed::$signer only gets defined in EncryptionServiceProvider, which leads me to believe this is some weird binding issue.
driesvints commented 2 years ago

This package is only compatible with Laravel unfortunately. Please switch to the Laravel framework if you need this.

MattApril commented 2 years ago

Thanks for reply. Does that mean batch callbacks are not supported in Lumen? Is see that the Lumen Queue docs state that "Closure jobs are not supported", but maybe that should be updated to specify batch callbacks as well then.

MattApril commented 2 years ago

I resolved this issue by registering the EncryptionServiceProvider on every bootstrap. In bootstrap/app.php: $app->register(\Illuminate\Encryption\EncryptionServiceProvider::class);

This ensures that every SerializedClosure will be signed and verified.