Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add session lock info #314 #342

Merged
merged 1 commit into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions classes/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static function reason_display(int $reason): string {
}

/**
* Returns a formatted time duration in m:s.ms format.
* Returns a formatted time duration in a human readable format.
*
* @param float $duration
* @param bool $markup If true, then use markup on the result.
Expand All @@ -104,10 +104,14 @@ public static function duration_display(float $duration, bool $markup = true): s
} else {
// Add one decimal place.
$usetime = round($duration, 1);
// Fallback case to prevent format_time() returning the translated string 'now' when $usetime is less than 100ms.
// It will still appear if less than 1ms rounded. Discuss.
if ($usetime < 0.1) {
$usetime = round($duration, 3);
// Fallback to prevent format_time returning the translated string 'now' when the rounded version is 0.
if ($usetime == 0) {
// Try rounding to 3 decimal places, otherwise return 0 secs.
if (round($duration, 3) > 0) {
$usetime = round($duration, 3);
} else {
return '-';
}
}
}
// This currently works with floats, but relies on undocumented behaviour of format_time(), which normally takes an int.
Expand Down Expand Up @@ -269,4 +273,41 @@ public static function course_display_name(int $courseid): string {

return $course->fullname;
}

/**
* Returns a HTML link based on the lockwait url.
* @param string|null $lockwaiturl Relative lockwait url
* @param float $lockwait Time spent waiting for the lock
* @return string html string
*/
public static function lockwait_display_link(?string $lockwaiturl, float $lockwait) {
if (empty($lockwaiturl)) {
return ($lockwait > 1) ? get_string('unknown', 'tool_excimer') : '-';
}

// Ideally we should link to an excimer profile of the url, but it's more reliable to link to group.
$profile = new profile();
$profile->add_env(script_metadata::get_normalised_relative_script_path($lockwaiturl, null));
$groupurl = new \moodle_url('slowest_web.php?group=' . $profile->get('scriptgroup'));

// Keep the link text short, but show the full url when hovering over it.
return \html_writer::link($groupurl, get_string('checkslowest', 'tool_excimer'), ['title' => $lockwaiturl]);
}

/**
* Returns lockwait help formatted after exporting for template.
* @param \renderer_base $output
* @param string $lockwaiturl lockwait display link url
* @return array export for template
*/
public static function lockwait_display_help(\renderer_base $output, string $lockwaiturl) {
GLOBAL $CFG;

// Only show help information if we have an 'Unknown' url and debug session lock is off.
if ($lockwaiturl === get_string('unknown', 'tool_excimer') && empty($CFG->debugsessionlock)) {
$help = new \help_icon('field_lockwaiturl', 'tool_excimer');
return $help->export_for_template($output);
}
return [];
}
}
5 changes: 4 additions & 1 deletion classes/profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,10 @@ protected static function define_properties(): array {
'dbwrites' => ['type' => PARAM_INT, 'default' => 0],
'dbreplicareads' => ['type' => PARAM_INT, 'default' => 0],
'lockreason' => ['type' => PARAM_TEXT, 'default' => ''],
'courseid' => ['type' => PARAM_INT, 'default' => null]
'courseid' => ['type' => PARAM_INT, 'default' => null],
'lockheld' => ['type' => PARAM_FLOAT, 'default' => 0],
'lockwait' => ['type' => PARAM_FLOAT, 'default' => 0],
'lockwaiturl' => ['type' => PARAM_TEXT, 'default' => ''],
];
}
}
18 changes: 17 additions & 1 deletion classes/profile_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class profile_table extends \table_sql {
/** Columns to be displayed. */
const COLUMNS = [
'duration',
'lockwait',
'responsecode',
'request',
'reason',
Expand Down Expand Up @@ -96,6 +97,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('responsecode', 'text-right');
$this->define_headers($headers);
}
Expand Down Expand Up @@ -187,7 +189,8 @@ protected function put_sql(): void {
'middlename',
'alternatename',
'lockreason',
'courseid'
'courseid',
'lockwait',
];
$fieldsstr = implode(',', $fields);

Expand Down Expand Up @@ -294,6 +297,19 @@ public function col_duration(\stdClass $record): string {
return helper::duration_display($record->duration, !$this->is_downloading());
}

/**
* Display value for 'lockwait' column entries.
*
* @param \stdClass $record
* @return string
*/
public function col_lockwait(\stdClass $record): string {
if (!isset($record->lockwait)) {
return get_string('unknown', 'tool_excimer');
}
return helper::duration_display($record->lockwait, !$this->is_downloading());
}

/**
* Display value for 'created' column entries.
*
Expand Down
1 change: 1 addition & 0 deletions classes/recent_profile_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class recent_profile_table extends profile_table {
'reason',
'type',
'duration',
'lockwait',
'userid',
'courseid'
];
Expand Down
31 changes: 31 additions & 0 deletions classes/script_metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,35 @@ public static function get_sample_limit(): int {
public static function get_stack_limit(): int {
return self::$stacklimit;
}

/**
* Returns information extracted from session lock info.
*
* @return array key value pairs that are ready to be saved to the profile
*/
public static function get_lock_info(): array {
$lockinfo = [];
$sessionlock = \core\session\manager::get_session_lock_info();
if (empty($sessionlock)) {
return $lockinfo;
}

// Get time spent waiting for lock and time spent holding a lock.
$lockinfo['lockwait'] = $sessionlock['wait'] ?? 0;

if (isset($sessionlock['held'])) {
$lockinfo['lockheld'] = $sessionlock['held'];
} else if (isset($sessionlock['gained'])) {
// Lock hasn't been released yet, so use current time as an estimate.
$lockinfo['lockheld'] = microtime(true) - $sessionlock['gained'];
}

// More info about the page holding the url is available when $CFG->debugsessionlock is set.
$url = \core\session\manager::get_locked_page_at($sessionlock['start']);
if (isset($url) && !empty($url['url'])) {
$lockinfo['lockwaiturl'] = $url['url'];
}

return $lockinfo;
}
}
3 changes: 3 additions & 0 deletions classes/web_processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public function process(manager $manager, bool $isfinal) {
$this->profile->set('flamedatad3', flamed3_node::from_excimer_log_entries($this->sampleset->samples));
$this->profile->set('numsamples', $this->sampleset->count());
$this->profile->set('samplerate', $this->sampleset->filter_rate() * $this->samplems);
foreach (script_metadata::get_lock_info() as $field => $value) {
$this->profile->set($field, $value);
}
$this->profile->save_record();
}
}
Expand Down
3 changes: 3 additions & 0 deletions db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
<FIELD NAME="samplerate" TYPE="int" LENGTH="11" NOTNULL="false" SEQUENCE="false" COMMENT="Base sample rate at the time the profile was recorded"/>
<FIELD NAME="lockreason" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Holds reasons for keeping this profile. Any non-empty value will prevent purging."/>
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The id of $COURSE at the time of profiling"/>
<FIELD NAME="lockheld" TYPE="number" LENGTH="12" NOTNULL="false" SEQUENCE="false" DECIMALS="6" COMMENT="Length of time a lock was held in seconds"/>
<FIELD NAME="lockwait" TYPE="number" LENGTH="12" NOTNULL="false" SEQUENCE="false" DECIMALS="6" COMMENT="Wait time on a lock in seconds"/>
<FIELD NAME="lockwaiturl" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="URL of the page holding the lock, if known"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand Down
31 changes: 31 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -459,5 +459,36 @@ function xmldb_tool_excimer_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2023082900, 'tool', 'excimer');
}

if ($oldversion < 2024050700) {

// Define field id to be added to tool_excimer_profiles.
$table = new xmldb_table('tool_excimer_profiles');
$field = new xmldb_field('lockheld', XMLDB_TYPE_NUMBER, '12, 6', null, null, null, null);

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

// Define field id to be added to tool_excimer_profiles.
$field = new xmldb_field('lockwait', XMLDB_TYPE_NUMBER, '12, 6', null, null, null, null);

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

// Define field id to be added to tool_excimer_profiles.
$field = new xmldb_field('lockwaiturl', XMLDB_TYPE_TEXT, null, null, null, null, null);

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

// Excimer savepoint reached.
upgrade_plugin_savepoint(true, 2024050700, 'tool', 'excimer');
}

return true;
}
7 changes: 7 additions & 0 deletions lang/en/tool_excimer.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@
$string['field_useragent'] = 'User agent';
$string['field_versionhash'] = 'Version Hash';
$string['field_courseid'] = 'Course';
$string['field_lockheld'] = 'Session held';
$string['field_lockwait'] = 'Session wait';
$string['field_lockwaiturl'] = 'Locking URL';
$string['field_lockwaiturl_help'] = 'Information about this page may only be available when $CFG->debugsessionlock is set.';

// Page count table.
$string['field_name'] = 'Name';
Expand Down Expand Up @@ -218,3 +222,6 @@
$string['duration'] = 'duration';
$string['no_month_in_page_group_table'] = 'Month value not set in page group table.';
$string['deletedcourse'] = 'Deleted course id: {$a}';
$string['unknown'] = 'Unknown';
$string['link'] = 'Direct link';
$string['lockwaitnotification'] = 'The majority of the duration was spent waiting for a session lock, the page may not be slow.';
18 changes: 17 additions & 1 deletion profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,23 @@
$data['created'] = userdate($data['created']);
$data['finished'] = userdate($data['finished']);

$data['duration'] = format_time(round($data['duration'], 3));
$duration = $data['duration'];
$data['duration'] = helper::duration_display($duration, true);
if (isset($data['lockwait'])) {
$lockwait = $data['lockwait'];
$data['lockwait'] = helper::duration_display($lockwait, true);
$data['lockheld'] = helper::duration_display($data['lockheld'], true);
$data['lockwaiturl'] = helper::lockwait_display_link($data['lockwaiturl'], $lockwait);
$data['lockwaiturlhelp'] = helper::lockwait_display_help($OUTPUT, $data['lockwaiturl']);

$processing = $duration - $lockwait;
if (($processing / $duration) < 0.1 && $processing < 10) {
\core\notification::warning(get_string('lockwaitnotification', 'tool_excimer'));
$data['waitnotification'] = true;
}
} else {
$data['lockwaiturl'] = '-';
}

$data['request'] = helper::full_request($profile->to_record());

Expand Down
21 changes: 21 additions & 0 deletions templates/profile.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@
<th>{{#str}} field_memoryusagemax, tool_excimer {{/str}}</th>
<td>{{memoryusagemax}}</td>
</tr>
<tr>
<th{{#waitnotification}} class="alert alert-warning"{{/waitnotification}}>{{#str}} field_lockwait, tool_excimer {{/str}}</th>
<td{{#waitnotification}} class="alert alert-warning"{{/waitnotification}}>
{{#lockwait}}{{{lockwait}}}{{/lockwait}}
{{^lockwait}}{{#str}} unknown, tool_excimer {{/str}}{{/lockwait}}
</td>
</tr>
<tr>
<th>{{#str}} field_lockheld, tool_excimer {{/str}}</th>
<td>
{{#lockheld}}{{{lockheld}}}{{/lockheld}}
{{^lockheld}}{{#str}} unknown, tool_excimer {{/str}}{{/lockheld}}
</td>
</tr>
</table>
</div>
<div class="">
Expand Down Expand Up @@ -170,6 +184,13 @@
<th>{{#str}} field_courseid, tool_excimer {{/str}}</th>
<td>{{{ course }}}</td>
</tr>
<tr>
<th>{{#str}} field_lockwaiturl, tool_excimer {{/str}}</th>
<td>
{{{lockwaiturl}}}
{{#lockwaiturlhelp}} {{>core/help_icon}} {{/lockwaiturlhelp}}
</td>
</tr>
</table>
</div>
</div>
Expand Down
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 = 2023082901;
$plugin->release = 2023082901;
$plugin->version = 2024050700;
$plugin->release = 2024050700;
$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.
Expand Down
Loading