Giter Club home page Giter Club logo

silverstripe-queuedjobs's Introduction

Silverstripe Queued Jobs Module

CI Silverstripe supported module

Overview

The Queued Jobs module provides a framework for Silverstripe developers to define long running processes that should be run as background tasks. This asynchronous processing allows users to continue using the system while long running tasks proceed when time permits. It also lets developers set these processes to be executed in the future.

The module comes with

  • A section in the CMS for viewing a list of currently running jobs or scheduled jobs.
  • An abstract skeleton class for defining your own jobs.
  • A task that is executed as a cronjob for collecting and executing jobs.
  • A pre-configured job to cleanup the QueuedJobDescriptor database table.

Installation

composer require symbiote/silverstripe-queuedjobs

Now setup a cron job:

*/1 * * * * /path/to/silverstripe/vendor/bin/sake dev/tasks/ProcessJobQueueTask
  • To schedule a job to be executed at some point in the future, pass a date through with the call to queueJob The following will run the publish job in 1 day's time from now.
use SilverStripe\ORM\FieldType\DBDatetime;
use Symbiote\QueuedJobs\Services\QueuedJobService;

$publish = new PublishItemsJob(21);
QueuedJobService::singleton()
    ->queueJob($publish, DBDatetime::create()->setValue(DBDatetime::now()->getTimestamp() + 86400)->Rfc2822());

Using Doorman for running jobs

Doorman is included by default, and allows for asynchronous task processing.

This requires that you are running an a unix based system, or within some kind of environment emulator such as cygwin.

In order to enable this, configure the ProcessJobQueueTask to use this backend.

In your YML set the below:

---
Name: localproject
After: '#queuedjobsettings'
---
SilverStripe\Core\Injector\Injector:
  Symbiote\QueuedJobs\Services\QueuedJobService:
    properties:
      queueRunner: '%$DoormanRunner'

Using Gearman for running jobs

---
Name: localproject
After: '#queuedjobsettings'
---
SilverStripe\Core\Injector\Injector:
  QueueHandler:
    class: Symbiote\QueuedJobs\Services\GearmanQueueHandler
  • Run the gearman worker using php gearman/gearman_runner.php in your SS root dir

This will cause all queuedjobs to trigger immediate via a gearman worker (src/workers/JobWorker.php) EXCEPT those with a StartAfter date set, for which you will STILL need the cron settings from above

Using QueuedJob::IMMEDIATE jobs

Queued jobs can be executed immediately (instead of being limited by cron's 1 minute interval) by using a file based notification system. This relies on something like inotifywait to monitor a folder (by default this is SILVERSTRIPE_CACHE_DIR/queuedjobs) and triggering the ProcessJobQueueTask as above but passing job=$filename as the argument. An example script is in queuedjobs/scripts that will run inotifywait and then call the ProcessJobQueueTask when a new job is ready to run.

Note - if you do NOT have this running, make sure to set QueuedJobService::$use_shutdown_function = true; so that immediate mode jobs don't stall. By setting this to true, immediate jobs will be executed after the request finishes as the php script ends.

Default Jobs

Some jobs should always be either running or queued to run, things like data refreshes or periodic clean up jobs, we call these Default Jobs. Default jobs are checked for at the end of each job queue process, using the job type and any fields in the filter to create an SQL query e.g.

ArbitraryName:
  type: 'ScheduledExternalImportJob'
  filter:
    JobTitle: 'Scheduled import from Services'

Will become:

QueuedJobDescriptor::get()->filter([
  'type' => 'ScheduledExternalImportJob',
  'JobTitle' => 'Scheduled import from Services'
]);

This query is checked to see if there's at least 1 healthly (new, run, wait or paused) job matching the filter. If there's not and recreate is true in the yml config we use the construct array as params to pass to a new job object e.g:

ArbitraryName:
  type: 'ScheduledExternalImportJob'
  filter:
    JobTitle: 'Scheduled import from Services'
  recreate: 1
  construct:
    repeat: 300
    contentItem: 100
      target: 157

If the above job is missing it will be recreated as:

Injector::inst()->createWithArgs(ScheduledExternalImportJob::class, $construct[])

Pausing Default Jobs

If you need to stop a default job from raising alerts and being recreated, set an existing copy of the job to Paused in the CMS.

YML config

Default jobs are defined in yml config the sample below covers the options and expected values

SilverStripe\Core\Injector\Injector:
  Symbiote\QueuedJobs\Services\QueuedJobService:
    properties:
      defaultJobs:
        # This key is used as the title for error logs and alert emails
        ArbitraryName:
          # The job type should be the class name of a job REQUIRED
          type: 'ScheduledExternalImportJob'
          # This plus the job type is used to create the SQL query REQUIRED
          filter:
            # 1 or more Fieldname: 'value' sets that will be queried on REQUIRED
            #  These can be valid ORM filter
            JobTitle: 'Scheduled import from Services'
          # Parameters set on the recreated object OPTIONAL
          construct:
            # 1 or more Fieldname: 'value' sets be passed to the constructor REQUIRED
            # If your constructor needs none, put something arbitrary
            repeat: 300
            title: 'Scheduled import from Services'
          # A date/time format string for the job's StartAfter field REQUIRED
          # The shown example generates strings like "2020-02-27 01:00:00"
          startDateFormat: 'Y-m-d H:i:s'
          # A string acceptable to PHP's date() function for the job's StartAfter field REQUIRED
          startTimeString: 'tomorrow 01:00'
          # Sets whether the job will be recreated or not OPTIONAL
          recreate: 1
          # Set the email address to send the alert to if not set site admin email is used OPTIONAL
          email: '[email protected]'
        # Minimal implementation will send alerts but not recreate
        AnotherTitle:
          type: 'AJob'
          filter:
            JobTitle: 'A job'

Configuring the CleanupJob

By default the CleanupJob is disabled. To enable it, set the following in your YML:

Symbiote\QueuedJobs\Jobs\CleanupJob:
  is_enabled: true

You will need to trigger the first run manually in the UI. After that the CleanupJob is run once a day.

You can configure this job to clean up based on the number of jobs, or the age of the jobs. This is configured with the cleanup_method setting - current valid values are "age" (default) and "number". Each of these methods will have a value associated with it - this is an integer, set with cleanup_value. For "age", this will be converted into days; for "number", it is the minimum number of records to keep, sorted by LastEdited. The default value is 30, as we are expecting days.

You can determine which JobStatuses are allowed to be cleaned up. The default setting is to clean up "Broken" and "Complete" jobs. All other statuses can be configured with cleanup_statuses. You can also define query_limit to limit the number of rows queried/deleted by the cleanup job (defaults to 100k).

The default configuration looks like this:

Symbiote\QueuedJobs\Jobs\CleanupJob:
  is_enabled: false
  query_limit: 100000
  cleanup_method: "age"
  cleanup_value: 30
  cleanup_statuses:
    - Broken
    - Complete

Jobs queue pause setting

It's possible to enable a setting which allows the pausing of the queued jobs processing. To enable it, add following code to your config YAML file:

Symbiote\QueuedJobs\Services\QueuedJobService:
  lock_file_enabled: true
  lock_file_path: '/shared-folder-path'

Queue settings tab will appear in the CMS settings and there will be an option to pause the queued jobs processing. If enabled, no new jobs will start running however, the jobs already running will be left to finish. This is really useful in case of planned downtime like queue jobs related third party service maintenance or DB restore / backup operation.

Note that this maintenance lock state is stored in a file. This is intentionally not using DB as a storage as it may not be available during some maintenance operations. Please make sure that the lock_file_path is pointing to a folder on a shared drive in case you are running a server with multiple instances.

One benefit of file locking is that in case of critical failure (e.g.: the site crashes and CMS is not available), you may still be able to get access to the filesystem and change the file lock manually. This gives you some additional disaster recovery options in case running jobs are causing the issue.

Health Checking

Jobs track their execution in steps - as the job runs it increments the "steps" that have been run. Periodically jobs are checked to ensure they are healthy. This asserts the count of steps on a job is always increasing between health checks. By default health checks are performed when a worker picks starts running a queue.

In a multi-worker environment this can cause issues when health checks are performed too frequently. You can disable the automatic health check with the following configuration:

Symbiote\QueuedJobs\Services\QueuedJobService:
  disable_health_check: true

In addition to the config setting there is a task that can be used with a cron to ensure that unhealthy jobs are detected:

*/5 * * * * /path/to/silverstripe/vendor/bin/sake dev/tasks/CheckJobHealthTask

Special job variables

It's good to be aware of special variables which should be used in your job implementation.

  • totalSteps (integer) - defaults to 0, maps to TotalSteps DB column, information only
  • currentStep (integer) - defaults to 0, maps to StepsProcessed DB column, Queue runner uses this to determine if job is stalled or not
  • isComplete (boolean) - defaults to false, related to JobStatus DB column, Queue runner uses this to determine if job is completed or not

See copyJobToDescriptor for more details on the mapping between Job and JobDescriptor.

Total steps

Represents total number of steps needed to complete the job.

  • this variable should be set to the number of steps you expect your job to go though during its execution
  • this needs to be done in the setup() function of your job, the value should not be changed after that
  • this variable is not used by the Queue runner and is only meant to indicate how many steps are needed (information only)
  • it is recommended to avoid using this variable inside the process() function of your job instead, determine if your job is complete based on the job data (if there are any more items left to process)

Current step

Represents number of steps processed.

  • your job should increment this variable each time a job step was successfully completed
  • Queue runner will read this variable to determine if your job is stalled or not
  • it is recommended to return out of the process() function each time you increment this variable
  • this allows the queue runner to create a checkpoint by saving your job progress into the job descriptor which is stored in the DB

Is complete

Represents the job state (complete or not).

  • setting this variable to true will give a signal to the queue runner to mark the job as successfully completed
  • your job should set this variable to true only once

Example

This example illustrates how each special variable should be used in your job implementation.

<?php

namespace App\Jobs;

use Symbiote\QueuedJobs\Services\AbstractQueuedJob;

/**
 * Class MyJob
 *
 * @property array $items
 * @property array $remaining
 */
class MyJob extends AbstractQueuedJob
{
    public function hydrate(array $items): void
    {
        $this->items = $items;
    }

    /**
     * @return string
     */
    public function getTitle(): string
    {
        return 'My awesome job';
    }

    public function setup(): void
    {
        $this->remaining = $this->items;

        // Set the total steps to the number of items we want to process
        $this->totalSteps = count($this->items);
    }

    public function process(): void
    {
        $remaining = $this->remaining;

        // check for trivial case
        if (count($remaining) === 0) {
            $this->isComplete = true;

            return;
        }

        $item = array_shift($remaining);

        // code that will process your item goes here
        $this->doSomethingWithTheItem($item);

        $this->remaining = $remaining;

        // Updating current step tells the Queue runner that the job is progressing
        $this->currentStep += 1;

        // check for job completion
        if (count($remaining) > 0) {
            // Note that we do not process more than one item at a time
            // this makes the Queue runner save the job progress into DB
            // in case something goes wrong the job will be resumed from the last checkpoint
            return;
        }

        // Queue runner will mark this job as finished
        $this->isComplete = true;
    }
}

Troubleshooting

To make sure your job works, you can first try to execute the job directly outside the framework of the queues - this can be done by manually calling the setup() and process() methods. If it works fine under these circumstances, try having getJobType() return QueuedJob::IMMEDIATE to have execution work immediately, without being persisted or executed via cron. If this works, next make sure your cronjob is configured and executing correctly.

If defining your own job classes, be aware that when the job is started on the queue, the job class is constructed without parameters being passed; this means if you accept constructor args, you must detect whether they're present or not before using them. See this issue and this wiki page for more information.

If defining your own jobs, please ensure you follow PSR conventions, i.e. use "YourVendor" rather than "SilverStripe".

Ensure that notifications are configured so that you can get updates or stalled or broken jobs. You can set the notification email address in your config as below:

SilverStripe\Control\Email\Email:
  queued_job_admin_email: [email protected]

Documentation

Show job data

In case you need an easy access to additonal job data via CMS for debug purposes enable the show_job_data option by including the configuration below.

Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor:
  show_job_data: true

This will add Job data and Messages raw tabs to the job descriptor edit form. Displayed information is read only.

Contributing

Translations

Translations of the natural language strings are managed through a third party translation interface, transifex.com. Newly added strings will be periodically uploaded there for translation, and any new translations will be merged back to the project source code.

Please use https://www.transifex.com/projects/p/silverstripe-queuedjobs to contribute translations, rather than sending pull requests with YAML files.

silverstripe-queuedjobs's People

Contributors

ajshort avatar andrewandante avatar assertchris avatar bergice avatar brettt89 avatar chillu avatar dhensby avatar dnsl48 avatar dorsetdigital avatar emteknetnz avatar firesphere avatar frankmullenger avatar gordonbanderson avatar guysartorelli avatar johannesx75 avatar mateusz avatar matt-in-a-hat avatar mattclegg avatar maxime-rainville avatar mfendeksilverstripe avatar micschk avatar mikeyc7m avatar nightjar avatar nyeholt avatar raissanorth avatar robbieaverill avatar scopeynz avatar sheadawson avatar torleif avatar wilr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

silverstripe-queuedjobs's Issues

Clear jobs from the db

So as not to clog up the db there should be a way to clear jobs from the db after a set interval, or after x completions. Use case being I have a job that runs every minute, over the course of a day thats 1440 new records.

Jobs should be able to set themselves to be paused

At present there's no way for a job implementation to mark itself as paused; it needs to search for a QueuedJobDescriptor and mark that as paused, or create a new job with the 'remainder' of things to process.

Running queuedjobs in multiple queue processors

Hi there,

Is it possible to process the queuedjobs in multiple workers? The issue that I currently have is that my backup system adds many many sites to the queuedjobs list and trying to backup 100+ sites on a single thread/worker is very slow and inefficient.

Is there anyway (with modification) this SIlverStripe modular can run on multiple queue processors?

Thanks,

Messages column in overview is XML safe

Thus the whole <ul><li> thing is escaped and shown as literal in the Message column.
I've tried a few things, but to no avail yet.

(Also, GitHub parses HTML, apparently! :D )

Split Doorman into a seperate module

When trying to setup PHPStan on a 3.6+ project, I was unable to install PHP-Parser because Doorman via SuperClosure, locks it at ~0.8 (or something around there).

How should a major version bump be handled for 3.X modules?

CMS requirement

Hi There,
Is there a specific reason why silverstripe-cms is included as a requirement? I've been using this module with a framework only project and it's working fine. I notice that the examples are mainly oriented towards CMS users however.

Thanks for your consideration.

Action 'EditForm' isn't allowed on class QueuedJobsAdmin

I was able to suppress the error by using getEditField() rather than EditField() and $form = parent::getEditForm(); rather than $form = parent::EditForm();. Have not got far enough to see if this impacts anything negatively, though.

Using with SilverStripe 3.1 Branch

Prevent stalled job email spam

If the cron for queued jobs runs every minute, this means am email is sent every minute. It would be nice to only send an email e.g. once every hour. Especially if it's for the same stalled job.

Thanks!

Adding jobs to Gearman JobQue pauses other running jobs

There seems to be a problem if you use gearman as the QueueHandler and have multiple workers.
When you have jobs running and then add multiple new jobs, the already running jobs get stalled.

From what I can tell this happens because JobWorker::jobqueueExecute calls checkJobHealth.
So everytime a new job is added (and picked up by a worker) the checkJobHealth runs. If you add jobs faster then your workers need for one step, they get paused.

I guess disabling health checks would be an option, but I would much prefer to still be able to check on jobs. Wasn't there an option once on how often the check should occur?

Alternatively the checkJobHealth call could be removed from the jobqueueExecute and instead be run in a much slower interval as a cron maybe?

Not transaction safe on concurrent use

QueuedJobService->grabMutex() has the following comment:

Given a {@link QueuedJobDescriptor} mark the job as initialised. Works sort of like a mutex.
Currently a database lock isn't entirely achievable, due to database adapters not supporting locks.
This may still have a race condition, but this should minimise the possibility.
Side effect is the job status will be changed to "Initialised".

Do you agree that with async/concurrent executions through the doorman worker, there's still a reasonable chance of race conditions (same job processed multiple times)? Suggestion: The SELECT call should create a read lock on this row, but that's not possible with the current ORM. As an alternative, we could use named locks with job identifiers through Database->getLock().

Either way, if this indeed a bug (and I'm not missing anything), it is important enough to point out in the bugtracker. Many job runs will have fairly strong assumptions on only being run once and won't implement additional safety checks in their own logic unless made aware of this existing limitation.

Running on Windows in Live mode fails

Hi,

I am using this module in a windows environment which is fine when the site is in 'dev' mode but once I switch it to live mode it does not run at all. I am using solr so occasionally I need to run /dev/task/Solr_Reindex but it never completes. If i switch to dev mode and run it i get the desired results. This does not just occur with other modules that use this as well.

Cheers

Custom execution interval

It seems an hour is the smallest interval available when using the ScheduledExecutionExtension.
I was wondering, is it possible to extend this to allow a minute interval, and ideally to accept a custom number of minutes?
Could this be added as a feature to QueuedJobs?

Removal of "2" branch

Just wanted to give a heads up that I'll remove the standalone "2" branch (2.9 will remain) at the end of the week. Any ongoing 2.x work will continue in a discrete minor version branch, any new features will see a 2.10 branch created at that appropriate time.

Jobs run concurrently with the same signature

I have several jobs that I don't want to run at the same time. Even though the job returns a static signature through the getSignature() function, they continue to run concurrently at the same time.

I'm setting isComplete = true when it completes, and I'm stepping through the currentSteps and have set the totalSteps correctly.

Move queue-handling specific code to DefaultQueueHandler class

Some of the DB specific aspects of queue handling (eg pseudo-mutexing) should be encapsulated better behind the QueueHandler interface.

This will also help with some aspects of how remote queue handlers manage things like $job->pause() (whether from mem issues or ui interaction) and $job->restart()

Run jobs via `/dev/jobs/MyJob` or similar

It’s not at all intuitive to write jobs or run them on a local machine. It needs a dev/tasks/MyTask equivalent like dev/jobs/MyJob that only runs your job (with defaults)

Currently there’s a task that does that but it procedes to run other jobs afterwards.
This adds friction to my development times.

How would it work?
It would either run an existing job of that type or create a new one if it does not exist with default parameters. I will also explore adding query parameters so you could go by ID, like:
dev/jobs/MyJob "ID=3" which would execute a queuedjob with that ID.

MySQL server has gone away

I'm using this plugin with the lucene plugin and I have quite a relatively big index, 4000 or so items. When running a re-index I get this 'mysql server has gone away' error. Not sure if this is an issue that can be solved in queuedjobs or maybe I need to tweak my specific set-up?

[2014-06-27 10:54:08] Running Rebuild the Lucene search engine index and others from 2.
 ERROR [User Error]: Couldn't run query: 
SELECT DISTINCT "QueuedJobDescriptor"."ClassName", "QueuedJobDescriptor"."Created", "QueuedJobDescriptor"."LastEdited", "QueuedJobDescriptor"."JobTitle", "QueuedJobDescriptor"."Signature", "QueuedJobDescriptor"."Implementation", "QueuedJobDescriptor"."StartAfter", "QueuedJobDescriptor"."JobStarted", "QueuedJobDescriptor"."JobRestarted", "QueuedJobDescriptor"."JobFinished", "QueuedJobDescriptor"."TotalSteps", "QueuedJobDescriptor"."StepsProcessed", "QueuedJobDescriptor"."LastProcessedCount", "QueuedJobDescriptor"."ResumeCounts", "QueuedJobDescriptor"."SavedJobData", "QueuedJobDescriptor"."SavedJobMessages", "QueuedJobDescriptor"."JobStatus", "QueuedJobDescriptor"."JobType", "QueuedJobDescriptor"."RunAsID", "QueuedJobDescriptor"."ID", CASE WHEN "QueuedJobDescriptor"."ClassName" IS NOT NULL THEN "QueuedJobDescriptor"."ClassName" ELSE 'QueuedJobDescriptor' END AS "RecordClassName"
FROM "QueuedJobDescriptor"
WHERE ("QueuedJobDescriptor"."JobStatus" = 'Waiting') AND ("QueuedJobDescriptor"."JobType" = '1')
LIMIT 1 

MySQL server has gone away
IN GET /dev/tasks/ProcessJobQueueTask
Line 598 in /var/www/vhosts/fosroc.com/beta1/framework/model/MySQLDatabase.php

Thanks.

Jobs getting stuck

Every once and a while I get a job that does not finish because of a flakey external API. This job then holds up the queue.

There needs to be a way to force jobs to finish

Should saved job messages be their own model?

The messages can be very useful for providing feedback, but they’re currently very difficult to read in the list view, and aren’t visible at all in the detail view. I see that we can’t pass it in __construct(), but perhaps we could inject the QueuedJobDescriptor into the job itself via a setDescriptor() method so we have access to the model for writing these to the database?

Pseudo-code:

// QueuedJobService.php
protected function initialiseJob(QueuedJobDescriptor $jobDescriptor) {
    $impl = $jobDescriptor->Implementation;
    $job = Object::create($impl);
    $job->setDescriptor($jobDescriptor); // <-- Add this
    // ... etc
}

// AbstractQueuedJob.php
public function addMessage($message, $severity = 'INFO') {
    $this->getDescriptor()->addMessage($message, $severity);
}

// QueuedJobDescriptor.php
public function addMessage($message, $severity) {
    $this->Messages()->add(
        'Message' => $message,
        'Severity' => $severity,
        'Datetime' => date('Y-m-d H:i:s')
    );
}

That’d allow us to provide a much nicer default UI for the messages, and make it easier to customise.

Thoughts?

3.2 compatibility

We should start testing modules against SilverStripe master, in preparation for the 3.2 release line and furthering its adoption. At the moment tests are failing due to static visibility.

SS4 code coverage runs have failures in integration tests

I've removed the standard code coverage run from the Travis config template (introduced in #139), which normally uses phpdbg on PHP 7.1 - I've done this because there are two integration tests that consistently fail with coverage runs enabled. Using Xdebug we still have a failure but in a different integration test.

The failing tests with phpdbg are:

1) Symbiote\QueuedJobs\Tests\QueuedJobsTest::testStartJob
Failed asserting that false is true.

2) Symbiote\QueuedJobs\Tests\QueuedJobsTest::testImmediateQueuedJob
Failed asserting that 2 matches expected 0.

with xdebug:

1) Symbiote\QueuedJobs\Tests\ScheduledExecutionTest::testScheduledExecutionInterval
Did not reschedule 1 minute later
Failed asserting that 0 matches expected 1.

We need to investigate what causes this code to fail with coverage drivers enabled so we can enable code coverage runs.

ProcessJobQueueTask remains in memory, causing issues after code deployment

On CWP, using default queuedjobs settings, the ProcessJobQueueTask never exits in some cases. After a deployment, the worker is left behind using the old code which remains in-memory, while the site, database, solr schema etc. have already moved on. This causes weird, hard-to-debug fatal errors in many cases.

We have cases where the worker has been running for several months already. The problem isn't easy to notice because the worker keeps picking up new tasks. Did not have a chance to find the exact trigger, but in one case there were Initialising jobs in the queue, in another there were Broken, Cancelled and Paused, in yet another just Broken and Paused.

I see two ways to resolve this:

  • Make an explicit promise the worker will exit after the current batch is done.
  • Expose an API for gracefully pausing and reloading the worker. This would allow us to pause jobs for the duration of deployment, and then reload when we are done.

I feel like the latter is better, because letting the worker run in parallel to the deployment seems like a recipe for trouble.

Here is how php-resque does it.

OOM errors leave queuedjobs unrecoverable

There is memory protection in queuedjobs, but it's more of a mechanism for reducing the chance of this happening rather than recovering from such a state.

What normally happens is that the job will have JobStatus = 'Running', and when the job detects that memory is low it sets the job to 'Waiting' and restarts.

If this catch fails to trigger then the job will be left with status = waiting and there won't be any indication that the queue has failed (unless you explicitly check the server logs for the error message).

Completed queued jobs in admin

Currently, queued jobs "disappear" after they've ran. It would be nice to have a completed jobs section, to view the messages. As people aren't keeping an eye on their system 24/7.

checkJobHealth doesn't distinguish between queues

It appears that QueuedJobService::checkJobHealth() doesn't take into account the current queue and therefore how long a job should be running before determining if it's stalled.

In the documented setup you'd have a cron running by the minute and by the quarter hour to run appropriate queues, however each of these crons will check for any task whatsoever that is "stalled" and will attempt to restart it.

This means that the "short" queue will try to run "long" jobs that appear to be "stalled" and the long task will be marked as paused quite quickly if progress isn't made fast enough.

This results in several processes running the same job and whatever side effects come with that.

Update asyncphp/doorman to ^3.0

Just logging this here to help alleviate a composer dependency chain breaking with the latest SS4 builds:

  • silverstripe/framework requires nikic/php-parser ^2 || ^3
  • silverstripe/activedirectory requires silverstripe/queuedjobs ^3.0@dev
  • silverstripe/queuedjobs requires asyncphp/doorman ~1.2
  • asyncphp/doorman requires jeremeamia/superclosure ^1.0
  • jeremeamia/superclosure requires nikic/php-parser ~0.9

This means you can't update the framework and CMS if you've got queuedjobs installed.

The latest version of asyncphp/doorman (3.0.0) requires superclosure ^2.0 which in its latest version (2.3.0) has a much looser constraint for php-parser: ^1.2 | ^2.0 | ^3.0, which would be compatible with the framework.

I'm not familiar with doorman, but can have a crack at updating this requirement and will PR a fix.

JobData doesn't accept arrays

The following code won't work in a QueuedJob

public function process() {
    if (!$this->myArray) {
        $this->myArray = array();
    }

    $this->myArray[] = 'myval';
}

Instead, just empty arrays are stored

Jobs that kill PHP left as "running"

When a job triggers a PHP fatal error, the job status is left as "running".

At a minimum, this occurs with a PHP parse error, if the reader struggles to find a replication case.

Ideally this could be cleaned up somehow. An register_shutdown function might be able to trap some errors but I'm not sure if it can grab everything, although this suggests it can pick up parser errors, which is promising.

Expected behaviour would be that jobs that die mid-way are marked with an error.

Authentication needed?

I can't get queuedjobs to work on a live server.
I setup the cron job (tried with cli-script.php and sake) and it appear to run, but every time i get this:

Status: 302 OK
X-Powered-By: PHP/5.3.8
Content-Type: text/html; charset="utf-8"
Location: /Security/login?BackURL=%2Fdev%2Ftasks%2FProcessJobQueueTask

That tell me that the request was redirect to login.

ProcessJobQueueTask.php listJobs() missing

In line 24 through 28 of ProcessJobQueueTask.php seems to be code for a list helper:

if($request->getVar('list')) {
    // List helper
    $this->listJobs();
    return;
}

Calling list on the task fails because the method 'listjobs' does not exist on 'ProcessJobQueueTask'.
Is this obsolete code or is this listJobs somewhere else and the call just needs to be updated?

Email template doesn't apply theme

Not sure if this is module-specific but emails sent from within a queuedjob appear to be ignorant of the theme. For example I have a template called GenericEmail.ss in my theme but instead of using that it sends using the framework version. Pages & tasks are using the correct themed file, queuedjobs are not.
$data = $data->renderWith('SomeCustomTemplate');
$email->setTemplate('GenericEmail');
$email->populateTemplate(array('Body'=>$data));
$email->send();
I tried updating SSViewer config to specify the theme but it had no effect. SS3.2

Minor change to GridField iconography

I'm no UX expert but the icon used to represent a job to be paused , looks like a "No Entry" sign. My suggestion is to change the pause icon to look like a pause button does on a CD player, and the current "Reload" icon, while better, should use a "Play" icon - again as per the CD player analogy for consistency.

Limit amount of running jobs

It would be nice, if there could be a limit to the amount of jobs running. e.g. when there are 4 processes running, don't start another one.

Add module to scrutinizer

Could you please add the module to scrutinizer? https://scrutinizer-ci.com/g/new. We're keeping track of all modules we use on our contributions dashboard (github-dashing.herokuapp.com), and I've added scrutinizer quality and coverage scores there. Doesn't require any config, just four clicks. You'll have to click "Schedule inspection" for the first run, after that it should kick off automatically on commits.

If you feel like it, you could also add me as a collaborator :) There's a "Add collaborators to your repository" option on the homepage.

We're experimenting with a more fine tuned config, including code coverage running on travis, but that's not really to roll out yet (https://github.com/stojg/silverstripe-timeline). Happy to hear feedback and get help of course :)

Feature: Ability to queue build tasks

The queuedjobs module should come out of the box with a facility for executing build tasks as a job. This would mean that sites that have a number of build tasks already written can easy shift to running these in a back-end queue.

My current use-case is to limit the need to SSH on a box to perform server-admin tasks, which often take the form of long-running build tasks.

Core features:

  • Create a RunBuildTaskJob job type that can execute any build task
  • Replace the default /dev/tasks UI to provide one that can enqueue a RunBuildTaskJob instead of running the job immediately

Optional features:

  • Provide an email address where the output of the buildtask will be sent.

Ensure multiple queue processors don't run duplicate jobs

Right now queuedjobs seems to rely on only 1 copy of the worker process running. If you have multiple workers running—e.g. for scaling, or because you have more than 1 server and it's easier to configure them the same—race conditions can lead to a single job being executed more than once.

It would be better if the queuedjobs module guarded against this. Presumably this would involve updating the status from the non-running state to the running state in a single command:

UPDATE "QueuedJobDescriptor" SET "JobStatus" = 'Running'
WHERE "JobStatus" IN ('New', 'Waiting') AND "ID" = $jobID

If this command didn't affect any rows, then we know someone else as handled the job and we can exit without running the job.

I guess this goes in here? https://github.com/silverstripe-australia/silverstripe-queuedjobs/blob/master/code/services/QueuedJobService.php#L395

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.