Skip to content

Commit

Permalink
feat: Add ugrade step config helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Peterburnett committed Mar 5, 2024
1 parent 94a5181 commit 5714fb4
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 37 deletions.
3 changes: 2 additions & 1 deletion classes/local/execution/engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ public function abort(?\Throwable $reason = null) {
$this->set_current_step($enginestep);
$enginestep->abort();
} else {
// We need to signal to finished steps that the dataflow is aborted. This may require handling seperate to the step abort.
// We need to signal to finished steps that the dataflow is aborted.
// This may require handling seperate to the step abort.
// This is done seperate to the finalise hook so that concerns are seperated for finalised vs aborted runs.
$this->set_current_step($enginestep);
$enginestep->dataflow_abort();
Expand Down
14 changes: 9 additions & 5 deletions classes/local/scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,19 @@ public static function set_scheduled_times(int $dataflowid, int $stepid, int $ne
* @param int $scheduledtime when to run next if allowed retries are exhausted.
* @param int $retriesallowed the amount of retries allowed before resuming regular schedule.
*/
public static function set_scheduled_retry(int $dataflowid, int $stepid, int $retrytime, int $scheduledtime, int $retriesallowed) {
global $DB;
public static function set_scheduled_retry(
int $dataflowid,
int $stepid,
int $retrytime,
int $scheduledtime,
int $retriesallowed) {

global $DB;
$schedule = $DB->get_record(self::TABLE, ['dataflowid' => $dataflowid, 'stepid' => $stepid]);

if (!$schedule) {
// This method has been called incorrectly for a schedule that has never run or doesn't exist.
// Just return early.
return;
throw new \coding_exception("Dataflow retry attempted on a trigger with no step.");
}

if ($schedule->retrycount >= $retriesallowed) {
Expand All @@ -93,7 +97,7 @@ public static function set_scheduled_retry(int $dataflowid, int $stepid, int $re
$schedule->nextruntime = $scheduledtime;
} else {
// Increment retry counter, and schedule the retry time.
$schedule->retrycount = $schedule->retrycount + 1;
$schedule->retrycount += 1;
$schedule->nextruntime = $retrytime;
}

Expand Down
4 changes: 2 additions & 2 deletions classes/local/step/trigger_cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public function form_get_default_data(\stdClass $data): \stdClass {
$data->{"config_$field"} = '*';
}
}
$data->config_retryinterval = $data->config_retryinterval ?? 0;
$data->config_retrycount = $data->config_retrycount ?? 0;
$data->config_retryinterval ??= 0;
$data->config_retrycount ??= 0;

return $data;
}
Expand Down
68 changes: 48 additions & 20 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

use Symfony\Component\Yaml\Yaml;

/**
* Function to upgrade tool_dataflows.
*
Expand Down Expand Up @@ -287,21 +289,6 @@ function xmldb_tool_dataflows_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2023072100, 'tool', 'dataflows');
}

if ($oldversion < 2024030201) {

// Define field retrycount to be added to tool_dataflows_schedule.
$table = new xmldb_table('tool_dataflows_schedule');
$field = new xmldb_field('retrycount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'nextruntime');

// Conditionally launch add field retrycount.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Dataflows savepoint reached.
upgrade_plugin_savepoint(true, 2024030201, 'tool', 'dataflows');
}

// Move log files that exist across to new format. Breaking change if any
// dataflows implement logic based on these files based on filename format.
if ($oldversion < 2023110901) {
Expand All @@ -319,19 +306,39 @@ function xmldb_tool_dataflows_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2023110901, 'tool', 'dataflows');
}

if ($oldversion < 2024030200) {

// Define field loghandlers to be added to tool_dataflows.
if ($oldversion < 2023110903) {
// Define field notifyonabort to be added to tool_dataflows.
$table = new xmldb_table('tool_dataflows');
$field = new xmldb_field('notifyonabort', XMLDB_TYPE_CHAR, '255', null, null, null, '', 'confighash');

// Conditionally launch add field loghandlers.
// Conditionally launch add field notifyonabort.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

upgrade_plugin_savepoint(true, 2023110903, 'tool', 'dataflows');
}

if ($oldversion < 2023110904) {
// Define field retrycount to be added to tool_dataflows_schedule.
$table = new xmldb_table('tool_dataflows_schedule');
$field = new xmldb_field('retrycount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'nextruntime');

// Conditionally launch add field retrycount.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Also upgrade any cron_trigger step.
$type = \tool_dataflows\local\step\trigger_cron::class;
$newfields = [
'retryinterval' => 0,
'retrycount' => 0
];
xmldb_tool_dataflows_step_config_helper($type, $newfields);

// Dataflows savepoint reached.
upgrade_plugin_savepoint(true, 2024030200, 'tool', 'dataflows');
upgrade_plugin_savepoint(true, 2023110904, 'tool', 'dataflows');
}

return true;
Expand Down Expand Up @@ -363,3 +370,24 @@ function xmldb_tool_dataflows_logfile_rename_helper(string $path, string $patter
}
}
}

/**
* Upgrade step helper function. Appends fields into existing configuration.
*
* @param string $type the classname of the step to upgrade.
* @param array $newfields new fields to append into the configuration array. String => primitive scalar.
*/
function xmldb_tool_dataflows_step_config_helper(string $type, array $newfields) {
global $DB;

$steps = $DB->get_records('tool_dataflows_steps', ['type' => $type]);
$transaction = $DB->start_delegated_transaction();
foreach ($steps as $step) {
$config = Yaml::parse($step->config);
$updatedconf = array_merge($config, $newfields);
$step->config = Yaml::dump($updatedconf);
$DB->update_record('tool_dataflows_steps', $step);
}
$transaction->allow_commit();
}

4 changes: 3 additions & 1 deletion tests/fixtures/sample-cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ steps:
day: '*'
month: '*'
dayofweek: '*'
retryinterval: 0
retrycount: 0
sql_reader:
name: 'SQL reader'
depends_on: cron
Expand All @@ -21,4 +23,4 @@ steps:
debugging_writer:
name: 'Debugging writer'
depends_on: sql_reader
type: tool_dataflows\local\step\writer_debugging
type: tool_dataflows\local\step\writer_debugging
2 changes: 1 addition & 1 deletion tests/tool_dataflows_ad_hoc_task_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private function create_dataflow() {
$cron = new step();
$cron->name = 'cron';
$cron->type = 'tool_dataflows\local\step\trigger_cron';
$cron->config = "minute: '*'\nhour: '*'\nday: '*'\nmonth: '*'\ndayofweek: '*'";
$cron->config = "minute: '*'\nhour: '*'\nday: '*'\nmonth: '*'\ndayofweek: '*'\nretryinterval: 0\nretrycount: 0";
$dataflow->add_step($cron);

$reader = new step();
Expand Down
25 changes: 20 additions & 5 deletions tests/tool_dataflows_scheduler_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace tool_dataflows;

use Aws\Arn\Arn;
use coding_exception;
use tool_dataflows\local\scheduler;

/**
Expand Down Expand Up @@ -71,12 +71,27 @@ public function test_update_next_scheduled_time() {
$this->assertEquals((object) ['lastruntime' => 160, 'nextruntime' => 220], scheduler::get_scheduled_times(12));
}

public function test_set_scheduled_retry() {
global $DB;

/**
* Tests the scheduling of retry run on an invalid dataflow.
*
* @covers \tool_dataflows\local\scheduler::set_scheduled_retry
*/
public function test_set_scheduled_retry_invalid() {
// Retry cannot be called for a run that hasn't been regularly scheduled.
$this->expectException(coding_exception::class);
$this->expectExceptionMessage(
'Coding error detected, it must be fixed by a programmer: Dataflow retry attempted on a trigger with no step.'
);
scheduler::set_scheduled_retry(1, 1, 1, 1, 1);
$this->assertFalse(scheduler::get_scheduled_times(1));
}

/**
* Tests the scheduling of retry runs.
*
* @covers \tool_dataflows\local\scheduler::set_scheduled_retry
*/
public function test_set_scheduled_retry() {
global $DB;

// Default 0.
scheduler::set_scheduled_times(1, 1, 123, 1);
Expand Down
71 changes: 71 additions & 0 deletions tests/tool_dataflows_upgrade_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace tool_dataflows;
use Symfony\Component\Yaml\Yaml;

defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../db/upgrade.php');

/**
* Unit test for the dataflows upgrade steps.
*
* @package tool_dataflows
* @author Peter Burnett <[email protected]>
* @copyright 2024, Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_dataflows_upgrade_test extends \advanced_testcase {
/**
* Test config upgrade helper.
*
* @covers \tool_dataflows\local\step\trigger_event::validate_config
*/
public function test_step_config_helper() {
global $DB;
$this->resetAfterTest();

$dataflow = new dataflow();
$dataflow->name = 'testupgrade';
$dataflow->enabled = true;
$dataflow->save();

$step = new step();
$step->name = 'cron';
$step->type = 'tool_dataflows\local\step\trigger_cron';
$config = [
'minute' => '*',
'hour' => '*',
'day' => '*',
'month' => '*',
'dayofweek' => '*',
'retryinterval' => '0',
'retrycount' => '0',
];
$step->config = Yaml::dump($config);
$dataflow->add_step($step);

// Regular Additional config.
$existingstep = $DB->get_record('tool_dataflows_steps', ['type' => $step->type]);
$this->assertEquals($config, Yaml::parse($existingstep->config));

$config['newstep'] = 'upgrade';
$newfields = ['newstep' => 'upgrade'];
xmldb_tool_dataflows_step_config_helper($step->type, $newfields);
$updatedrec = $DB->get_record('tool_dataflows_steps', ['type' => $step->type]);
$this->assertEquals($config, Yaml::parse($updatedrec->config));
}
}
4 changes: 2 additions & 2 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2023110902;
$plugin->release = 2023110902;
$plugin->version = 2023110904;
$plugin->release = 2023110904;
$plugin->requires = 2017051500; // Our lowest supported Moodle (3.3.0).
$plugin->supported = [35, 401]; // Available as of Moodle 3.9.0 or later.
// TODO $plugin->incompatible = ; // Available as of Moodle 3.9.0 or later.
Expand Down

0 comments on commit 5714fb4

Please sign in to comment.