Right approach to handle pending queue jobs at upgrade #9868
Replies: 3 comments 7 replies
-
Extra notesFor the #1 and #3, even if we're careful about our structures, internal changes to Laravel might leave us broken (our job class isn't a plain old PHP object). Palliative for #1 and #3If we only use native structures (integers, strings, array, ...), serialization issues should be highly reduced on our side. |
Beta Was this translation helpful? Give feedback.
-
Besides the jobs, @asmecher has mentioned that we have other types of serializations (perhaps at the invitations?!), I don't have a clear opinion about options without seeing the code. The cache is also a good source of such problems, but I think it was already addressed. |
Beta Was this translation helpful? Give feedback.
-
I started to tinker with a complicated runtime solution involving traits that define exactly what gets serialized for a job. This would signal to the coder that they need to take care when changing the trait that defines the serialized data. However, it got complicated and started to introduce some "magic" behaviour of a kind I don't like. If all we need to do is catch a coder who inadvertently changes serialization expectations of a job or invitation, breaking older data, why don't we just solve it through unit tests? Idea: When we introduce a new job or invitation, we make sure to add a test that includes its serialized form. If the implementation later changes in a breaking way, an exception or error should be thrown, catching the issue before release. Example: A unit test for the <?php
/**
* @file tests/jobs/submissions/UpdateSubmissionSearchJobTest.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @brief Tests for the submission search reindexing job.
*/
namespace PKP\tests\classes\core;
use PKP\tests\PKPTestCase;
use PKP\jobs\submissions\UpdateSubmissionSearchJob;
use Mockery;
class UpdateSubmissionSearchJobTest extends PKPTestCase
{
/**
* Ensure that a serialized job can be unserialized and executed
*/
public function testRunSerializedJob()
{
// Serializion from OJS 3.4.0 for a submission reindex job with submission ID 17
$updateSubmissionSearchJob = unserialize('O:46:"PKP\jobs\submissions\UpdateSubmissionSearchJob":3:{s:15:"^@*^@submissionId";i:17;s:10:"connection";s:8:"database";s:5:"queue";s:5:"queue";}');
// Ensure that the unserialized object is the right class
$this->assertInstanceOf(UpdateSubmissionSearchJob::class, $updateSubmissionSearchJob);
// Mock the Submission facade to return a fake submission when Repo::submission()->get($id) is called
$mock = Mockery::mock(app(\APP\submission\Repository::class))
->makePartial()
->shouldReceive('get')
->with(17) // Submission ID from serialization string
->andReturn(new \APP\submission\Submission())
->getMock();
app()->instance(\APP\submission\Repository::class, $mock);
// Test that the job can be handled without causing an exception.
$updateSubmissionSearchJob->handle();
}
} Notes:
|
Beta Was this translation helpful? Give feedback.
-
With the release of
3.4.0
, we have introduced the queue jobs to handle long running process in background . These jobs are stored in database(set driver, currently only support database driver) in serialized format and later retrieved from database and reconstruct the jobs class from the serialized data . As for these sole reason, it is important to maintain the consistency of the job class structure through out the application release cycle . With the slight change of the jobs class structure e.g. the changes/remove/add of class properties may cause the jobs not the execute it's desired action .However with each application version release cycle, with the continuous update/bug fix/feature addition to the application, it's normal for job class to end up with different class structure based on requirement. Now if a pending job with different class structure remains in the driver and then in the upgraded version it try to execute that version where the jobs class structure has changed , it may cause the jobs not to be able to execute and fail continuously .
To overcome the above issue we need to have some mechanism so that installation does not end up with older/previous (before update) pending jobs in the system that continue to fail before up change in class structure and cause undesired outcome .
Solution 1:
Before upgrade, we can force to complete the execution of any pending jobs in the system . we have that mechanism built in and a check the preflight check will ask to execute any pending jobs in the system and will not allow to upgrade if there are any pending job in the system .
Solution 2:
We will never touch any of the exiting jobs class but if needed to update the job class structure, have different job class such as
JobClass
,JobClass_2
etc . This way a previous version such asJobClass
will continue to execute . if after upgrade, the previous version is not needed to run or if may cause issue when it run, we can simply update thehandle
method and just return from there . However that implies that developer take proper care to make sure they do not make any changes to current job class structure, only update the handle method as need to facilitate the update and create a new one to handle updated requirements .Solution 3:
At the upgrade time, we will check each of the pending job class with the existing class structure and if any mismatch found , try to update the serialized data to make it compatible with the new class structure . This process can be done in upgrade preflight check . However this will be a CPU/Memory intensive task and hard to determine the possible compatible values for update/added properties in job class .
Beta Was this translation helpful? Give feedback.
All reactions