Skip to content
Snippets Groups Projects
Commit e54ff4e2 authored by Alexey Korepov's avatar Alexey Korepov
Browse files

Issue #3402309: Cover by unit tests in Gitlab CI

parent 0f5db8f3
No related branches found
No related tags found
1 merge request!1Issue #3402309: Cover by unit tests in Gitlab CI
Pipeline #51471 passed
# Resource usage meter
Adds to HTTP headers of a response the performance data of the request not only
as an actual execution time (`rtime`), but also the `utime` and `stime`, which
represent the actual CPU time, spent on the request separately by the current
php process and system processes.
as the actual execution time in seconds (`rtime`), but also the `utime` and
`stime`, which represent the actual CPU time, spent on the request separately
by the current php process and system processes. Additionally, it shows the
memory usage and the disk read (`inblock`) and write (`oublock`) operations.
An example of the HTTP headers output of a response:
```
x-rusage-rtime: 3.482499
x-rusage-stime: 0.101052
x-rusage-utime: 2.792771
x-rusage-memory: 91076728
x-rusage-memory-peak: 144699680
x-rusage-inblock: 16216
x-rusage-oublock: 192
```
An example chart for 10 concurrent requests to the homepage:
![Drupal rusage_meter chart example](https://www.drupal.org/files/project-images/drupal-rusage_meter-chart_example.png)
The data is collected by the PHP funciton `getrusage()`.
The data is collected by the PHP functions `getrusage()`, `microtime()`,
`memory_get_usage()`, `memory_get_peak_usage()`.
## Features
......@@ -40,7 +46,8 @@ The module works out of the box without any configuration, and is controlled by
the environment variables:
- `RUSAGE_METER_INJECT_ALWAYS=1` - enables injecting headers always.
- `RUSAGE_METER_INJECT_KEY=put-an-unique-key-here` - enables injecting headers
only for requests with the GET query parameter `rusage-meter-key` or the HTTP header `x-rusage-meter-key` equals to the configured string.
only for requests with the GET query parameter `rusage-meter-key` or the HTTP
header `x-rusage-meter-key` equals to the configured string.
By default, it counts the resource usage of the span from the request processing
start event (`KernelEvents::REQUEST`) time to the response created event
......@@ -64,6 +71,18 @@ measure the exact part of your business logic, by using the service:
// Do some stuff.
\Drupal::service('rusage_meter')->setFinalData(array $data = NULL, bool $override = TRUE);
```
If the `$data` array is null, it will get the current values, if not null - put
your custom values.
And if you want to optimize some specific part of your code before the Drupal
kernel is initialized, for example, the autoloader, you can wrap it by requiring
the initial and final data, like this:
```php
+ require 'modules/contrib/rusage_meter/rusage_meter_initial.inc.php';
$autoloader = require_once 'autoload.php';
+ require 'modules/contrib/rusage_meter/rusage_meter_final.inc.php';
```
## Additional Requirements
......@@ -81,7 +100,8 @@ I haven't found any similar module, if you know one - please tell me!
## Supporting this Module
You can convey gratitude to me for the development of the module and motivate me to do more through these services:
You can convey gratitude to me for the development of the module and motivate me
to do more through these services:
[![Coindrop.to me](https://www.drupal.org/files/coindrop-embed-button.png)](https://coindrop.to/murz)
[![Buy Me a Coffee](https://www.drupal.org/files/by-me-a-coffee-button.png)](https://www.buymeacoffee.com/murz)
......@@ -3,7 +3,7 @@
"description": "Adds the resource usage info (rusage using getrusage() and memory usage) of each request to the resonse headers.",
"type": "drupal-module",
"require-dev": {
"drupal/test_helpers": "^1.3@beta"
"drupal/test_helpers": "^1.3@RC"
},
"authors": [
{
......
......@@ -34,6 +34,19 @@ class RusageMeterService implements EventSubscriberInterface {
*/
const ENV_INJECT_QUERY_PARAMETER = 'rusage-meter-key';
/**
* Header names for specific data.
*/
const PREFIX_HEADER = 'x-';
const HEADER_RESPONSE_TIME = self::PREFIX_HEADER . 'rusage-rtime';
const HEADER_UTIME = self::PREFIX_HEADER . 'rusage-utime';
const HEADER_STIME = self::PREFIX_HEADER . 'rusage-stime';
const HEADER_MEMORY_USAGE = self::PREFIX_HEADER . 'rusage-memory';
const HEADER_MEMORY_PEAK_USAGE = self::PREFIX_HEADER . 'rusage-memory-peak';
const HEADER_INBLOCK = self::PREFIX_HEADER . 'rusage-inblock';
const HEADER_OUBLOCK = self::PREFIX_HEADER . 'rusage-oublock';
/**
* The initial rusage data in the `getrusage()` format.
*
......@@ -154,16 +167,16 @@ class RusageMeterService implements EventSubscriberInterface {
* A response.
*/
private function addResponseHeaders(Response $response): void {
$response->headers->set('x-rusage-rtime', round($this->finalData[RusageMeter::KEY_MICROTIME] - $this->initialData[RusageMeter::KEY_MICROTIME], 6));
$response->headers->set('x-rusage-utime', $this->getDiff($this->initialData, $this->finalData, 'ru_utime'));
$response->headers->set('x-rusage-stime', $this->getDiff($this->initialData, $this->finalData, 'ru_stime'));
$response->headers->set('x-rusage-memory', $this->finalData[RusageMeter::KEY_MEMORY_USAGE] - $this->initialData[RusageMeter::KEY_MEMORY_USAGE]);
$response->headers->set('x-rusage-memory-peak', $this->finalData[RusageMeter::KEY_MEMORY_PEAK_USAGE] - $this->initialData[RusageMeter::KEY_MEMORY_PEAK_USAGE]);
$response->headers->set(self::HEADER_RESPONSE_TIME, round($this->finalData[RusageMeter::KEY_MICROTIME] - $this->initialData[RusageMeter::KEY_MICROTIME], 6));
$response->headers->set(self::HEADER_UTIME, $this->getDiff($this->initialData, $this->finalData, 'ru_utime'));
$response->headers->set(self::HEADER_STIME, $this->getDiff($this->initialData, $this->finalData, 'ru_stime'));
$response->headers->set(self::HEADER_MEMORY_USAGE, $this->finalData[RusageMeter::KEY_MEMORY_USAGE] - $this->initialData[RusageMeter::KEY_MEMORY_USAGE]);
$response->headers->set(self::HEADER_MEMORY_PEAK_USAGE, $this->finalData[RusageMeter::KEY_MEMORY_PEAK_USAGE] - $this->initialData[RusageMeter::KEY_MEMORY_PEAK_USAGE]);
// The ru_inblock and ru_oublock is not available on all OS, so we need to
// put the 'n/a' for such cases.
$response->headers->set('x-rusage-inblock', isset($this->initialData['ru_inblock']) ? ($this->finalData['ru_inblock'] - $this->initialData['ru_inblock']) : 'n/a');
$response->headers->set('x-rusage-oublock', isset($this->initialData['ru_oublock']) ? ($this->finalData['ru_oublock'] - $this->initialData['ru_oublock']) : 'n/a');
$response->headers->set(self::HEADER_INBLOCK, isset($this->initialData['ru_inblock']) ? ($this->finalData['ru_inblock'] - $this->initialData['ru_inblock']) : 'n/a');
$response->headers->set(self::HEADER_OUBLOCK, isset($this->initialData['ru_oublock']) ? ($this->finalData['ru_oublock'] - $this->initialData['ru_oublock']) : 'n/a');
}
/**
......@@ -177,7 +190,7 @@ class RusageMeterService implements EventSubscriberInterface {
if (!$enabled && $key = getenv(self::ENV_INJECT_KEY)) {
if (
$key == $request->query->get(self::ENV_INJECT_QUERY_PARAMETER)
|| $key == $request->headers->get('x-' . self::ENV_INJECT_QUERY_PARAMETER)
|| $key == $request->headers->get(self::PREFIX_HEADER . self::ENV_INJECT_QUERY_PARAMETER)
) {
$enabled = TRUE;
}
......
......@@ -104,30 +104,30 @@ class RusageMeterServiceTest extends UnitTestCase {
* @covers ::isEnabled
*/
public function testInjectRusageData() {
$this->assertArrayNotHasKey('x-rusage-utime', $this->getResponseEventHeaders());
$this->assertArrayNotHasKey(RusageMeterService::HEADER_UTIME, $this->getResponseEventHeaders());
putenv(RusageMeterService::ENV_INJECT_ALWAYS . '=1');
$this->assertTrue($this->getResponseEventHeaders()['x-rusage-utime'] > 0);
$this->assertTrue($this->getResponseEventHeaders()[RusageMeterService::HEADER_UTIME] > 0);
putenv(RusageMeterService::ENV_INJECT_ALWAYS . '=0');
$this->assertArrayNotHasKey('x-rusage-utime', $this->getResponseEventHeaders());
$this->assertArrayNotHasKey(RusageMeterService::HEADER_UTIME, $this->getResponseEventHeaders());
putenv(RusageMeterService::ENV_INJECT_KEY . '=foo');
$this->assertArrayNotHasKey('x-rusage-utime', $this->getResponseEventHeaders());
$this->assertArrayNotHasKey(RusageMeterService::HEADER_UTIME, $this->getResponseEventHeaders());
$request = new Request([RusageMeterService::ENV_INJECT_QUERY_PARAMETER => 'foo']);
$this->assertTrue($this->getResponseEventHeaders($request)['x-rusage-utime'] > 0);
$this->assertTrue($this->getResponseEventHeaders($request)[RusageMeterService::HEADER_UTIME] > 0);
$request = new Request([RusageMeterService::ENV_INJECT_QUERY_PARAMETER => 'bar']);
$this->assertArrayNotHasKey('x-rusage-utime', $this->getResponseEventHeaders($request));
$this->assertArrayNotHasKey(RusageMeterService::HEADER_UTIME, $this->getResponseEventHeaders($request));
$request = new Request();
$request->headers->set('x-' . RusageMeterService::ENV_INJECT_QUERY_PARAMETER, 'foo');
$this->assertTrue($this->getResponseEventHeaders($request)['x-rusage-utime'] > 0);
$request->headers->set(RusageMeterService::PREFIX_HEADER . RusageMeterService::ENV_INJECT_QUERY_PARAMETER, 'foo');
$this->assertTrue($this->getResponseEventHeaders($request)[RusageMeterService::HEADER_UTIME] > 0);
$request = new Request();
$request->headers->set('x-' . RusageMeterService::ENV_INJECT_QUERY_PARAMETER, 'bar');
$this->assertArrayNotHasKey('x-rusage-utime', $this->getResponseEventHeaders($request));
$request->headers->set(RusageMeterService::PREFIX_HEADER . RusageMeterService::ENV_INJECT_QUERY_PARAMETER, 'bar');
$this->assertArrayNotHasKey(RusageMeterService::HEADER_UTIME, $this->getResponseEventHeaders($request));
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment