diff --git a/classes/grouped_session_profile_table.php b/classes/grouped_session_profile_table.php new file mode 100644 index 0000000..21d1b91 --- /dev/null +++ b/classes/grouped_session_profile_table.php @@ -0,0 +1,119 @@ +. + +namespace tool_excimer; + +/** + * Table for displaying session lock info from profiles grouped by script. + * + * @package tool_excimer + * @author Benjamin Walker + * @copyright 2024, Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class grouped_session_profile_table extends grouped_script_profile_table { + + /** Columns to be displayed.*/ + const COLUMNS = [ + 'maxlockheld', + 'minlockheld', + 'requestcount', + 'maxcreated', + 'mincreated', + ]; + + /** @var int Only display maximum lockhelds above the threshold. */ + protected $lockheldthreshold; + + /** + * Sets the minimum lockheld threshold. + * + * @param int $duration + */ + public function set_lockheld_threshold(int $duration) { + $this->lockheldthreshold = $duration; + } + + /** + * Sets the SQL for the table. + */ + protected function put_sql(): void { + list($filterstring, $filterparams) = $this->get_filter_for_sql(); + + $this->set_count_sql( + "SELECT count(distinct request) + FROM {tool_excimer_profiles} + WHERE $filterstring", + $filterparams + ); + + $groupby = $this->get_group_by(); + + $filterstring .= " AND " . $groupby . " IS NOT NULL GROUP BY " . $groupby; + + if ($this->lockheldthreshold) { + $filterstring .= " HAVING MAX(lockheld) > ?"; + $filterparams = array_merge($filterparams, [$this->lockheldthreshold]); + } + + $this->set_sql( + $groupby . ', COUNT(request) as requestcount, MAX(created) as maxcreated, MIN(created) as mincreated, + MAX(lockheld) as maxlockheld, MIN(lockheld) as minlockheld', + '{tool_excimer_profiles}', + $filterstring, + $filterparams + ); + } + + /** + * Defines the columns for this table. + * + * @throws \coding_exception + */ + public function make_columns(): void { + $headers = []; + $columns = $this->get_columns(); + foreach ($columns as $column) { + $headers[] = get_string('field_' . $column, 'tool_excimer'); + } + + $this->define_columns($columns); + $this->column_class('maxlockheld', 'text-right'); + $this->column_class('minlockheld', 'text-right'); + $this->column_class('requestcount', 'text-center'); + $this->define_headers($headers); + } + + /** + * Max lockheld column. + * + * @param \stdClass $record + * @return string + */ + public function col_maxlockheld(\stdClass $record): string { + return helper::duration_display($record->maxlockheld, !$this->is_downloading()); + } + + /** + * Min lockheld column. + * + * @param \stdClass $record + * @return string + */ + public function col_minlockheld(\stdClass $record): string { + return helper::duration_display($record->minlockheld, !$this->is_downloading()); + } +} diff --git a/classes/output/tabs.php b/classes/output/tabs.php index 4633268..625032d 100644 --- a/classes/output/tabs.php +++ b/classes/output/tabs.php @@ -66,6 +66,12 @@ public function export_for_template(\renderer_base $output): array { 'title' => get_string('tab_page_course', 'tool_excimer'), 'text' => get_string('tab_page_course', 'tool_excimer'), ], + [ + 'id' => 'session_locks', + 'link' => [['link' => new \moodle_url('/admin/tool/excimer/session_locks.php')]], + 'title' => get_string('report_session_locks', 'tool_excimer'), + 'text' => get_string('tab_session_locks', 'tool_excimer'), + ], [ 'id' => 'recent', 'link' => [['link' => new \moodle_url('/admin/tool/excimer/recent.php')]], diff --git a/classes/profile_table.php b/classes/profile_table.php index 2dd473f..0689ba6 100644 --- a/classes/profile_table.php +++ b/classes/profile_table.php @@ -98,6 +98,7 @@ public function make_columns(): void { $this->define_columns($columns); $this->column_class('duration', 'text-right'); $this->column_class('lockwait', 'text-right'); + $this->column_class('lockheld', 'text-right'); $this->column_class('responsecode', 'text-right'); $this->define_headers($headers); } @@ -191,6 +192,7 @@ protected function put_sql(): void { 'lockreason', 'courseid', 'lockwait', + 'lockheld', ]; $fieldsstr = implode(',', $fields); @@ -310,6 +312,19 @@ public function col_lockwait(\stdClass $record): string { return helper::duration_display($record->lockwait, !$this->is_downloading()); } + /** + * Display value for 'lockheld' column entries. + * + * @param \stdClass $record + * @return string + */ + public function col_lockheld(\stdClass $record): string { + if (!isset($record->lockheld)) { + return get_string('unknown', 'tool_excimer'); + } + return helper::duration_display($record->lockheld, !$this->is_downloading()); + } + /** * Display value for 'created' column entries. * diff --git a/classes/session_profile_table.php b/classes/session_profile_table.php new file mode 100644 index 0000000..1907f62 --- /dev/null +++ b/classes/session_profile_table.php @@ -0,0 +1,54 @@ +. + +namespace tool_excimer; + +/** + * Display table for profile reports with session lock information. + * + * @package tool_excimer + * @author Benjamin Walker + * @copyright 2024, Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class session_profile_table extends profile_table { + + /** Columns to be displayed */ + const COLUMNS = [ + 'duration', + 'lockheld', + 'responsecode', + 'request', + 'reason', + 'type', + 'created', + 'userid', + 'courseid', + ]; + + /** + * returns the columns defined for the table. + * + * @return string[] + */ + protected function get_columns(): array { + $columns = self::COLUMNS; + if (!$this->is_downloading()) { + $columns[] = 'actions'; + } + return $columns; + } +} diff --git a/lang/en/tool_excimer.php b/lang/en/tool_excimer.php index 5ac2542..97d2a08 100644 --- a/lang/en/tool_excimer.php +++ b/lang/en/tool_excimer.php @@ -33,6 +33,7 @@ $string['report_slowest_grouped'] = 'Slowest scripts'; $string['report_slowest_web'] = 'Slow web pages'; $string['report_slowest_other'] = 'Slow tasks / CLI / WS'; +$string['report_session_locks'] = 'Long session locks'; $string['report_recent'] = 'Recently profiled'; $string['report_unfinished'] = 'Currently profiling'; $string['report_page_groups'] = 'Page Group Metadata'; @@ -101,6 +102,7 @@ $string['slowest'] = 'Slowest'; $string['tab_slowest_web'] = 'Slow web pages'; $string['tab_slowest_other'] = 'Slow tasks / CLI / WS'; +$string['tab_session_locks'] = 'Long session locks'; $string['unfinished'] = 'Unfinished'; $string['tab_page_groups'] = 'Page Groups'; $string['tab_page_course'] = 'Courses'; @@ -144,6 +146,8 @@ $string['field_mincreated'] = 'Earliest'; $string['field_maxduration'] = 'Slowest'; $string['field_minduration'] = 'Fastest'; +$string['field_maxlockheld'] = 'Longest'; +$string['field_minlockheld'] = 'Shortest'; $string['field_requestcount'] = 'Num profiles'; $string['field_pid'] = 'Process ID'; $string['field_hostname'] = 'Host name'; diff --git a/session_locks.php b/session_locks.php new file mode 100644 index 0000000..f780941 --- /dev/null +++ b/session_locks.php @@ -0,0 +1,61 @@ +. + +/** + * Long session lock data in a table. + * + * @package tool_excimer + * @author Benjamin Walker + * @copyright 2024, Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_excimer\grouped_session_profile_table; +use tool_excimer\profile_table_page; +use tool_excimer\session_profile_table; + +require_once('../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +// The script can either be a URL, or a task name, or whatever may be used +// for a request name. So we need to accept TEXT input. +$script = optional_param('script', '', PARAM_TEXT); +$group = optional_param('group', '', PARAM_TEXT); + +admin_externalpage_setup('tool_excimer_report_session_locks'); + +$url = new moodle_url("/admin/tool/excimer/session_locks.php"); + +if ($script || $group) { + $table = new session_profile_table('profile_table_session_locks'); + $table->sortable(true, 'lockheld', SORT_DESC); + if ($script) { + $table->add_filter('request', $script); + $url->params(['script' => $script]); + } + if ($group) { + $table->add_filter('scriptgroup', $group); + $url->params(['group' => $group]); + $PAGE->navbar->add($group); + } +} else { + $table = new grouped_session_profile_table('profile_table_session_locks'); + $table->set_url_path($url); + $table->set_lockheld_threshold(1); + $table->sortable(true, 'maxlockheld', SORT_DESC); +} + +profile_table_page::display($table, $url); diff --git a/settings.php b/settings.php index 4f6e843..58ad5f4 100644 --- a/settings.php +++ b/settings.php @@ -285,4 +285,14 @@ function ($v) { 'moodle/site:config' ) ); + + $ADMIN->add( + 'tool_excimer_reports', + new admin_externalpage( + 'tool_excimer_report_session_locks', + get_string('report_session_locks', 'tool_excimer'), + new moodle_url('/admin/tool/excimer/session_locks.php'), + 'moodle/site:config' + ) + ); } diff --git a/version.php b/version.php index 5092cf4..2ae9ce0 100644 --- a/version.php +++ b/version.php @@ -25,8 +25,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024082301; -$plugin->release = 2024082301; +$plugin->version = 2024091000; +$plugin->release = 2024091000; $plugin->requires = 2017051500; // Moodle 3.3 for Totara support. $plugin->supported = [35, 401]; // Supports Moodle 3.5 or later. // TODO $plugin->incompatible = ; // Available as of Moodle 3.9.0 or later.