Skip to content

Commit 4e320a2

Browse files
Added monitorManager
Added metrics Added prometheus response handling Added response size toggle for speed vs memory optimalization of response decoding.
1 parent cb3ba84 commit 4e320a2

18 files changed

+2056
-20
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ The admin manager manages administrative functions and information retrieval for
7070
$client->admin()->version();
7171
```
7272

73+
### Monitor manager
74+
The monitor manager manages monitoring functions the server/cluster.
75+
```
76+
$client->monitor()->getMetrics();
77+
```
78+
7379
### Schema manager
7480
The schema manager manages all schema related operations.
7581
```
@@ -86,15 +92,16 @@ $client->transactions()->begin(['write' => ['users', 'teams']]);
8692
1) [ArangoDB PHP client](docs/arangodb-client.md)
8793
2) [AQL query statements](docs/statements.md)
8894
3) [Admin manager](docs/admin-manager.md)
89-
4) Schema manager
95+
4) [Monitor manager](docs/monitor-manager.md)
96+
5Schema manager
9097
1) [Database schema](docs/schema-databases.md)
9198
2) [User schema](docs/schema-users.md)
9299
3) [Collection schema](docs/schema-collections.md)
93100
4) [Index schema](docs/schema-indexes.md)
94101
5) [Graph schema](docs/schema-graphs.md)
95102
6) [View schema](docs/schema-views.md)
96103
7) [Analyzer schema](docs/schema-analyzers.md)
97-
5) [Transaction manager](docs/transaction-manager.md)
104+
6[Transaction manager](docs/transaction-manager.md)
98105

99106
## Related packages
100107
* [AQL query builder](https://github.com/LaravelFreelancerNL/fluentaql)

docs/arangodb-client.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Upon creation, you can alter the default configuration of the client. The follow
1313
* username = null
1414
* password = null
1515
* database = '_system'
16+
* responseSizeDecoderSwitch = 1 * 1024 * 1024
1617

1718
```
1819
$config = [
@@ -25,6 +26,15 @@ $config = [
2526
$arangoClient = new ArangoClient($config);
2627
```
2728

29+
### Speed vs response size
30+
JSON response decoding is normally done by the default json_decode method. This method
31+
is optimized for speed and can take a large amount of memory; up to ~ 20x of the JSON size.
32+
33+
Therefor we use halaxa/json-machine to stream decode for responses larger than 1MB.
34+
You can alter this cutoff by setting the `responseSizeDecoderSwitch` to a different size in **Bytes**.
35+
36+
This removed any memory issues at the cost of speed.
37+
2838
### Support Guzzle configuration
2939
In addition to the above mentioned options you can use the following Guzzle 7 specific options:
3040
* [version](https://docs.guzzlephp.org/en/stable/request-options.html#version)

docs/monitor-manager.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Monitor manager
2+
3+
Manages monitoring functions for the server/cluster.
4+
5+
## Functions
6+
The monitor manager supports the following functions:
7+
8+
### getMetrics(): Metrics
9+
Get Prometheus metrics of the server
10+
11+
```
12+
$arangoClient->monitor()->getMetrics();
13+
```

phpstan.neon.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ parameters:
22
level: 8
33
paths:
44
- src
5+
universalObjectCratesClasses:
6+
- ArangoClient\Prometheus\Metrics

src/ArangoClient.php

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626
class ArangoClient
2727
{
28+
use HandlesResponses;
2829
use HandlesJson;
2930
use HasManagers;
3031
use SupportsTransactions;
@@ -88,7 +89,11 @@ public function request(string $method, string $uri, array|HttpRequestOptions $o
8889
$this->handleGuzzleException($e);
8990
}
9091

91-
return $this->cleanupResponse($response);
92+
if ($response !== null) {
93+
return $this->cleanupResponse($response);
94+
}
95+
96+
return new stdClass();
9297
}
9398

9499
/**
@@ -116,7 +121,7 @@ public function debugRequest(
116121
string $method,
117122
string $uri,
118123
array $options = [],
119-
?string $database = null,
124+
?string $database = null
120125
): ResponseInterface {
121126
$uri = $this->prependDatabaseToUri($uri, $database);
122127
$options['debug'] = true;
@@ -142,23 +147,31 @@ protected function handleGuzzleException(Throwable $e): void
142147
$code = $e->getCode();
143148

144149
if ($e instanceof RequestException && $e->hasResponse()) {
145-
$decodedResponse = $this->decodeResponse($e->getResponse());
146-
$message = (string) $decodedResponse->errorMessage;
147-
$code = (int) $decodedResponse->code;
150+
$response = $e->getResponse();
151+
if ($response !== null) {
152+
$decodedResponse = $this->decodeResponse($response);
153+
}
154+
if (isset($decodedResponse->errorMessage)) {
155+
$message = (string) $decodedResponse->errorMessage;
156+
}
157+
158+
if (isset($decodedResponse->code)) {
159+
$code = (int) $decodedResponse->code;
160+
}
148161
}
149162

150163
throw(
151-
new ArangoException(
152-
$code . ' - ' . $message,
153-
$code,
154-
)
164+
new ArangoException(
165+
$code . ' - ' . $message,
166+
$code
167+
)
155168
);
156169
}
157170

158171
/**
159172
* @SuppressWarnings(PHPMD.StaticAccess)
160173
*/
161-
protected function cleanupResponse(?ResponseInterface $response): stdClass
174+
protected function cleanupResponse(ResponseInterface $response): stdClass
162175
{
163176
$response = $this->decodeResponse($response);
164177
unset($response->error);
@@ -175,16 +188,19 @@ protected function cleanupResponse(?ResponseInterface $response): stdClass
175188
public function prepare(
176189
string $query,
177190
array $bindVars = [],
178-
array $options = [],
191+
array $options = []
179192
): Traversable {
180193
return new Statement($this, $query, $bindVars, $options);
181194
}
182195

183196
/**
184-
* @return array<array-key, mixed>
197+
* @return mixed
185198
*/
186-
public function getConfig(): array
199+
public function getConfig(string $value = null): mixed
187200
{
201+
if ($value) {
202+
return $this->config->$value;
203+
}
188204
return $this->config->toArray();
189205
}
190206

src/HandlesJson.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,18 @@ public function jsonEncode(mixed $data): string
3535
/**
3636
* @SuppressWarnings(PHPMD.StaticAccess)
3737
*/
38-
protected function decodeResponse(?ResponseInterface $response): stdClass
38+
protected function decodeJsonResponse(ResponseInterface $response): stdClass
3939
{
40-
$decodedResponse = new stdClass();
41-
if (!isset($response)) {
42-
return $decodedResponse;
40+
$contentLength = $response->getHeaderLine('Content-Length');
41+
$sizeSwitch = $this->getConfig('responseSizeDecoderSwitch');
42+
if ($contentLength < $sizeSwitch) {
43+
return json_decode($response->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR);
4344
}
4445

46+
$decodedResponse = new stdClass();
47+
4548
$phpStream = StreamWrapper::getResource($response->getBody());
46-
$decoder = new ExtJsonDecoder(false);
49+
$decoder = new ExtJsonDecoder(true);
4750
$decodedStream = Items::fromStream($phpStream, ['decoder' => $decoder]);
4851

4952
foreach ($decodedStream as $key => $value) {

src/HandlesResponses.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArangoClient;
6+
7+
use ArangoClient\Prometheus\Prometheus;
8+
use Psr\Http\Message\ResponseInterface;
9+
use stdClass;
10+
11+
trait HandlesResponses
12+
{
13+
use HandlesJson;
14+
15+
/**
16+
* @SuppressWarnings(PHPMD.StaticAccess)
17+
*/
18+
protected function decodeResponse(ResponseInterface $response): stdClass
19+
{
20+
$contentType = null;
21+
$rawContentType = $response->getHeader('Content-type');
22+
if (is_array($rawContentType) && sizeOf($rawContentType) > 0) {
23+
$contentType = $rawContentType[0];
24+
}
25+
26+
return match($contentType) {
27+
"application/json; charset=utf-8" => $this->decodeJsonResponse($response),
28+
"text/plain; charset=utf-8" => $this->decodeTextResponse($response),
29+
default => (object) $response->getBody()->getContents(),
30+
};
31+
}
32+
33+
/**
34+
* @SuppressWarnings(PHPMD.StaticAccess)
35+
*/
36+
protected function decodeTextResponse(ResponseInterface $response): stdClass
37+
{
38+
$prometheus = new Prometheus();
39+
return $prometheus->parseStream($response);
40+
}
41+
}

src/HasManagers.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
namespace ArangoClient;
66

77
use ArangoClient\Admin\AdminManager;
8+
use ArangoClient\Monitor\MonitorManager;
89
use ArangoClient\Schema\SchemaManager;
910

1011
trait HasManagers
1112
{
1213
protected ?AdminManager $adminManager = null;
1314

15+
protected ?MonitorManager $monitorManager = null;
16+
1417
protected ?SchemaManager $schemaManager = null;
1518

1619
public function admin(): AdminManager
@@ -22,6 +25,15 @@ public function admin(): AdminManager
2225
return $this->adminManager;
2326
}
2427

28+
public function monitor(): MonitorManager
29+
{
30+
if (!(property_exists($this, 'monitorManager') && $this->monitorManager !== null)) {
31+
$this->monitorManager = new MonitorManager($this);
32+
}
33+
34+
return $this->monitorManager;
35+
}
36+
2537
public function schema(): SchemaManager
2638
{
2739
if (!(property_exists($this, 'schemaManager') && $this->schemaManager !== null)) {

src/Http/HttpClientConfig.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ class HttpClientConfig extends DataTransferObject
3737

3838
public string $database = '_system';
3939

40+
/**
41+
* Small responses are decoded with json_decode. This is fast but memory intensive.
42+
* Large responses are decoded with Halaxa/json-machine stream decoder.
43+
* $responseSizeDecoderSwitch is the response length cutoff in bytes which determines which decoder is used.
44+
*
45+
* @var int
46+
*/
47+
public int $responseSizeDecoderSwitch = 1 * 1024 * 1024; // Default 1 MB
48+
4049
/**
4150
* @return array<array<mixed>|string|numeric|bool|null>
4251
*/

src/Monitor/MonitorManager.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArangoClient\Monitor;
6+
7+
use ArangoClient\ArangoClient;
8+
use ArangoClient\Manager;
9+
10+
class MonitorManager extends Manager
11+
{
12+
public function __construct(protected ArangoClient $arangoClient) {}
13+
14+
public function getMetrics(): \stdClass
15+
{
16+
$uri = '/_admin/metrics/v2';
17+
18+
return $this->arangoClient->request('get', $uri);
19+
}
20+
}

src/Prometheus/Bucket.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArangoClient\Prometheus;
6+
7+
class Bucket
8+
{
9+
/**
10+
* @param int|float|null $value
11+
* @param array<string, mixed>|null $labels
12+
*/
13+
public function __construct(
14+
public int|float|null|string $value,
15+
public array|null $labels,
16+
public int|float|null|string $timestamp,
17+
) {}
18+
}

src/Prometheus/Metric.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArangoClient\Prometheus;
6+
7+
class Metric
8+
{
9+
/**
10+
* @param string $name
11+
* @param null|string $type
12+
* @param string|null $help
13+
* @param int|float|null $count
14+
* @param int|float|null $sum
15+
* @param int|float|null $value
16+
* @param array<array-key, Bucket>|null $buckets
17+
* @param array<string, float|int|string>|null $labels
18+
*/
19+
public function __construct(
20+
public string $name,
21+
public null|string $type = null,
22+
public null|string $help = null,
23+
public int|float|null $count = null,
24+
public int|float|null $sum = null,
25+
public int|float|null $value = null,
26+
public ?array $buckets = [],
27+
public ?array $labels = [],
28+
) {}
29+
}

src/Prometheus/Metrics.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArangoClient\Prometheus;
6+
7+
use stdClass;
8+
9+
class Metrics extends stdClass {}

0 commit comments

Comments
 (0)