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

Migrate to simdjson + perf tune #101

Merged
merged 3 commits into from
Jan 17, 2025
Merged
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
4 changes: 2 additions & 2 deletions src/ManticoreSearch/Client.php
Original file line number Diff line number Diff line change
@@ -414,7 +414,7 @@ public function getSettings(): Settings {
protected function fetchSettings(): Settings {
$resp = $this->sendRequest('SHOW SETTINGS');
/** @var array{0:array{columns:array<mixed>,data:array{Setting_name:string,Value:string}}} */
$data = (array)json_decode($resp->getBody(), true);
$data = (array)simdjson_decode($resp->getBody(), true);
$settings = new Vector();
foreach ($data[0]['data'] as ['Setting_name' => $key, 'Value' => $value]) {
// If the key is plugin_dir check env first and after choose
@@ -442,7 +442,7 @@ protected function fetchSettings(): Settings {
// Gather variables also
$resp = $this->sendRequest('SHOW VARIABLES');
/** @var array{0:array{columns:array<mixed>,data:array{Setting_name:string,Value:string}}} */
$data = (array)json_decode($resp->getBody(), true);
$data = (array)simdjson_decode($resp->getBody(), true);
foreach ($data[0]['data'] as ['Variable_name' => $key, 'Value' => $value]) {
$settings->push(
new Map(
20 changes: 13 additions & 7 deletions src/Network/Request.php
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
use Manticoresearch\Buddy\Core\ManticoreSearch\RequestFormat;
use Manticoresearch\Buddy\Core\ManticoreSearch\Settings;
use Manticoresearch\Buddy\Core\Tool\Buddy;
use SimdJsonException;

final class Request {
const PAYLOAD_FIELDS = [
@@ -81,6 +82,7 @@ public static function default(string $id = '0'): static {
* @param string $id
* @return static
*/

public static function fromString(string $data, string $id = '0'): static {
$self = new static;
$self->id = $id;
@@ -149,17 +151,21 @@ public static function validateOrFail(string $data): array {
if ($data === '') {
throw new InvalidNetworkRequestError('The payload is missing');
}
try {
$result = simdjson_decode($data, true, 512);
if (!is_array($result)) {
throw new InvalidNetworkRequestError('Invalid request payload is passed');
}
} catch (SimdJsonException) {
throw new InvalidNetworkRequestError('Failed to parse request payload');
}

/** @var array{
* type:string,
* error:array{message:string,body?:array{error:string}},
* message:array{path_query:string,body:string},
* version:int} $result
*/
$result = json_decode($data, true, 512, JSON_INVALID_UTF8_SUBSTITUTE);
if (!is_array($result)) {
throw new InvalidNetworkRequestError('Invalid request payload is passed');
}

return $result;
}

@@ -234,11 +240,11 @@ protected function parseOrFail(array $payload): static {
$this->path = $path;
$this->format = $format;
$this->endpointBundle = $endpointBundle;
$this->mySQLTool = static::detectMySQLTool($payload['message']['body']);
$this->mySQLTool = $format === RequestFormat::SQL ? static::detectMySQLTool($payload['message']['body']) : null;
$this->payload = (in_array($endpointBundle, [Endpoint::Elastic, Endpoint::Bulk]))
? trim($payload['message']['body'])
: static::removeComments($payload['message']['body']);
$this->command = strtok(strtolower($this->payload), ' ') ?: '';
$this->command = strtolower(strtok($this->payload, ' ') ?: '');
$this->error = $payload['error']['message'];
$this->errorBody = $payload['error']['body'] ?? [];
$this->version = match ($payload['version']) {
7 changes: 3 additions & 4 deletions src/Network/Struct.php
Original file line number Diff line number Diff line change
@@ -111,9 +111,7 @@ public function addBigIntField(string $field): void {
* @return bool
*/
public static function isValid(string $json): bool {
// TODO: replace with json_validate once we more to 8.3
$result = json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS);
return !!$result;
return simdjson_is_valid($json, static::JSON_DEPTH);
}

/**
@@ -123,9 +121,10 @@ public static function isValid(string $json): bool {
*/
public static function fromJson(string $json): self {
/** @var array<TKey, TValue> */
$result = (array)json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS);
$result = (array)simdjson_decode($json, true, static::JSON_DEPTH);
$bigIntFields = [];
if (static::hasBigInt($json)) {
// We need here to keep original json decode cuzit has bigIntFields
/** @var array<TKey, TValue> */
$modified = json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS | JSON_BIGINT_AS_STRING);
static::traverseAndTrack($modified, $result, $bigIntFields);
6 changes: 3 additions & 3 deletions src/Plugin/Pluggable.php
Original file line number Diff line number Diff line change
@@ -219,7 +219,7 @@ public function getList(): array {
return [];
}
/** @var array{require?:array<string,string>} $composerJson */
$composerJson = json_decode($composerContent, true);
$composerJson = simdjson_decode($composerContent, true);
if (!isset($composerJson['require'])) {
return [];
}
@@ -274,7 +274,7 @@ public function getClassNamespaceByFullName(string $name): string {
throw new Exception("Failed to get contents of composer.json for plugin: $name");
}

$composerJson = json_decode($composerContent, true);
$composerJson = simdjson_decode($composerContent, true);
if (!$composerJson) {
throw new Exception("Failed to decode contents of composer.json file for plugin: $name");
}
@@ -465,7 +465,7 @@ public function fetchLocalPlugins(): array {
throw new Exception("Failed to read composer file for plugin: $shortName");
}
/** @var array{name:?string} */
$composer = json_decode($composerContent, true);
$composer = simdjson_decode($composerContent, true);
if (!isset($composer['name'])) {
throw new Exception("Failed to detect local plugin name from file: $composerFile");
}
2 changes: 1 addition & 1 deletion src/Tool/KeyboardLayout.php
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ protected static function getLangMap(): array {
}

/** @var array<string,array<string>> $langMap */
$langMap = json_decode($configContent, true);
$langMap = simdjson_decode($configContent, true);
if (!is_array($langMap)) {
throw new Exception("Invalid keyboard layout config file at '$configPath'");
}
1 change: 0 additions & 1 deletion src/Tool/SqlQueryParser.php
Original file line number Diff line number Diff line change
@@ -82,7 +82,6 @@ private static function getCreator(): PHPSQLCreator {
* @throws GenericError
*/
public static function parse(string $payload, Closure $preProcessorCallback, mixed $args): ?array {

$result = (bool)call_user_func($preProcessorCallback, $args);
if ($result === false) {
return null;
9 changes: 7 additions & 2 deletions test/BuddyCore/Network/RequestTest.php
Original file line number Diff line number Diff line change
@@ -169,14 +169,19 @@ public function testManticoreQueryValidationFail(): void {
$this->assertEquals(InvalidNetworkRequestError::class, $exCls);
$this->assertEquals('The payload is missing', $exMsg);

$query = "Invalid query\nis passed\nagain";
$query = '"payload as a string"';
[$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]);
$this->assertEquals(InvalidNetworkRequestError::class, $exCls);
$this->assertEquals('Invalid request payload is passed', $exMsg);

$query = "Invalid query\nis passed\nagain";
[$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]);
$this->assertEquals(InvalidNetworkRequestError::class, $exCls);
$this->assertEquals('Failed to parse request payload', $exMsg);

$query = 'Query\nwith unvalid\n\n{"request_body"}';
[$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]);
$this->assertEquals(InvalidNetworkRequestError::class, $exCls);
$this->assertEquals('Invalid request payload is passed', $exMsg);
$this->assertEquals('Failed to parse request payload', $exMsg);
}
}
6 changes: 3 additions & 3 deletions test/src/Trait/TestFunctionalTrait.php
Original file line number Diff line number Diff line change
@@ -318,13 +318,13 @@ protected static function runHttpQuery(

/** @var array<int,array{error:string,data:array<int,array<string,string>>,total?:string,columns?:string}> $result */
$result = match ($path) {
'cli_json', 'sql', 'sql?mode=raw' => (array)json_decode(implode(PHP_EOL, $output), true),
'cli_json', 'sql', 'sql?mode=raw' => (array)simdjson_decode(implode(PHP_EOL, $output), true),
'cli' => [
['columns' => implode(PHP_EOL, $output), 'data' => [], 'error' => ''],
],
// assuming Elastic-like endpoint is passed
default => [
['data' => [(array)json_decode($output[0] ?? '{}', true)], 'error' => ''],
['data' => [(array)simdjson_decode($output[0] ?? '{}', true)], 'error' => ''],
],
};
print_r($output);
@@ -361,7 +361,7 @@ protected static function runHttpBuddyRequest(
$redirect = $redirectOutput ? '2>&1' : '';
exec("curl -s 127.0.0.1:$port -H 'Content-type: application/json' -d @$payloadFile $redirect", $output);
/** @var array{version:int,type:string,message:array<int,array{columns:array<string>,data:array<int,array<string,string>>}>} $result */
$result = (array)json_decode($output[0] ?? '{}', true);
$result = (array)simdjson_decode($output[0] ?? '{}', true);
return $result;
}