diff --git a/app/Consumer/PersonConsumer.php b/app/Consumer/PersonConsumer.php new file mode 100644 index 00000000..836b770d --- /dev/null +++ b/app/Consumer/PersonConsumer.php @@ -0,0 +1,14 @@ +driver = $driverFactory->get('default'); + } + + /** + * @throws \Throwable + */ public function create(PersonRequest $request, ResponseInterface $response): MessageResponseInterface { $person = $request->toPerson(); - $person->saveOrFail(); - - return $response->json($person)->withStatus(201); + + if ($this->redis->get($person['nick'])) { + return $response->json(['message' => 'Esse apelido já existe'])->withStatus(422); + } + + $this->driver->push(new PersonJob($person)); + +// + + $this->redis->set($person['nick'], '.'); + $this->redis->set($person['id'], json_encode($person)); + + return $response->json($person)->withStatus(201)->withHeader('Location', "/pessoas/{$person['id']}"); } - public function show(RequestInterface $request, ResponseInterface $response): MessageResponseInterface + public function show(RequestInterface $request, ResponseInterface $response, string $id): MessageResponseInterface { - return $response->raw('Hello Hyperf!'); + $cached = $this->redis->get($id); + if ($cached) { + + $person = json_decode($cached); + + return $response->json($person); + } + $response->raw('Not found')->withStatus(404); } public function search(RequestInterface $request, ResponseInterface $response): MessageResponseInterface { - return $response->raw('Hello Hyperf!'); + $term = $request->getQueryParams()['t'] ?? null; + if ($term) { + $list = Person::where(function (Builder $query) use ($term) { + $query->whereRaw("lower(name) like lower(\"%{$term}%\")") + ->orWhereRaw("lower(nick) like lower(\"%{$term}%\")") + ->orWhereRaw("lower(stack) like lower(\"%{$term}%\")"); + })->limit(50)->get(); + + return $response->json($list); + } + + return $response->json(['message' => 'Precisa de um termo na busca'])->withStatus(400); } public function count(RequestInterface $request, ResponseInterface $response): MessageResponseInterface { - return $response->raw('Hello Hyperf!'); + $count = Person::count(); + return $response->json(['count' => $count]); } } diff --git a/app/Job/PersonJob.php b/app/Job/PersonJob.php new file mode 100644 index 00000000..a7f1b981 --- /dev/null +++ b/app/Job/PersonJob.php @@ -0,0 +1,24 @@ +params = $params; + } + + public function handle() + { + Db::table('person')->insert($this->params); + } +} \ No newline at end of file diff --git a/app/Model/Person.php b/app/Model/Person.php index 8bfeb4b5..dd285698 100644 --- a/app/Model/Person.php +++ b/app/Model/Person.php @@ -31,7 +31,10 @@ class Person extends Model /** * The attributes that should be cast to native types. */ - protected array $casts = []; + protected array $casts = [ + 'id' => 'string', + 'stack' => 'array' + ]; protected array $attributes = [ 'stack' => '[]', diff --git a/app/Request/PersonRequest.php b/app/Request/PersonRequest.php index bc262256..756b2ad3 100644 --- a/app/Request/PersonRequest.php +++ b/app/Request/PersonRequest.php @@ -15,6 +15,7 @@ use App\Model\Person; use Hyperf\Codec\Json; use Hyperf\Validation\Request\FormRequest; +use Ramsey\Uuid\Uuid; final class PersonRequest extends FormRequest { @@ -40,16 +41,16 @@ public function rules(): array ]; } - public function toPerson(): Person + public function toPerson(): array { $data = $this->validated(); - return Person::create([ - 'id' => random_bytes(16), + return [ + 'id' => Uuid::uuid4()->toString(), 'nick' => $data['apelido'], 'name' => $data['nome'], 'birth' => $data['nascimento'], 'stack' => Json::encode($data['stack'] ?? null), - ]); + ]; } } diff --git a/composer.json b/composer.json index 60f762bf..697f5800 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "license": "MIT", "require": { "php": ">=8.0", + "hyperf/async-queue": "^3.0", "hyperf/cache": "^3.0", "hyperf/command": "^3.0", "hyperf/config": "^3.0", @@ -26,7 +27,8 @@ "hyperf/process": "^3.0", "hyperf/redis": "^3.0", "hyperf/translation": "^3.0", - "hyperf/validation": "^3.0" + "hyperf/validation": "^3.0", + "ramsey/uuid": "^4.7" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.0", diff --git a/composer.lock b/composer.lock index 21756cdd..96c2b297 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,63 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5c9924ee4c10ad8dc6ce42f5765a71c7", + "content-hash": "70395e32b08664a1a7f30bcd30136250", "packages": [ + { + "name": "brick/math", + "version": "0.11.0", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "5.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.11.0" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-01-15T23:15:59+00:00" + }, { "name": "doctrine/deprecations", "version": "v1.1.1", @@ -802,6 +857,70 @@ ], "time": "2023-08-03T15:06:02+00:00" }, + { + "name": "hyperf/async-queue", + "version": "v3.0.18", + "source": { + "type": "git", + "url": "https://github.com/hyperf/async-queue.git", + "reference": "7e7b558492a80ac29baeffe1942709a062d087aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hyperf/async-queue/zipball/7e7b558492a80ac29baeffe1942709a062d087aa", + "reference": "7e7b558492a80ac29baeffe1942709a062d087aa", + "shasum": "" + }, + "require": { + "hyperf/codec": "~3.0.0", + "hyperf/collection": "~3.0.0", + "hyperf/command": "~3.0.0", + "hyperf/contract": "~3.0.0", + "hyperf/support": "~3.0.0", + "hyperf/utils": "~3.0.0", + "php": ">=8.0", + "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0" + }, + "suggest": { + "hyperf/di": "Required to use annotations.", + "hyperf/event": "Required to dispatch a event.", + "hyperf/logger": "Required to use QueueHandleListener.", + "hyperf/process": "Auto register the consumer process for server." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "hyperf": { + "config": "Hyperf\\AsyncQueue\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Hyperf\\AsyncQueue\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A async queue component for hyperf.", + "homepage": "https://hyperf.io", + "keywords": [ + "async-queue", + "hyperf", + "php" + ], + "support": { + "docs": "https://hyperf.wiki", + "issues": "https://github.com/hyperf/hyperf/issues", + "pull-request": "https://github.com/hyperf/hyperf/pulls", + "source": "https://github.com/hyperf/hyperf" + }, + "time": "2023-04-26T03:02:31+00:00" + }, { "name": "hyperf/cache", "version": "v3.0.32", @@ -4109,6 +4228,187 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.4", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "60a4c63ab724854332900504274f6150ff26d286" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286", + "reference": "60a4c63ab724854332900504274f6150ff26d286", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.4" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2023-04-15T23:01:58+00:00" + }, { "name": "symfony/console", "version": "v6.3.2", diff --git a/config/autoload/async_queue.php b/config/autoload/async_queue.php new file mode 100644 index 00000000..27a58907 --- /dev/null +++ b/config/autoload/async_queue.php @@ -0,0 +1,10 @@ + [ + 'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class, + 'channel' => 'queue', + 'retry_seconds' => 5, + 'processes' => 1, + ], +]; \ No newline at end of file diff --git a/config/autoload/processes.php b/config/autoload/processes.php index 55aa4211..fa0dfa2b 100644 --- a/config/autoload/processes.php +++ b/config/autoload/processes.php @@ -10,4 +10,5 @@ * @license https://github.com/opencodeco/rinha-de-backend-2023-q3/blob/dev/LICENSE */ return [ + \Hyperf\AsyncQueue\Process\ConsumerProcess::class ]; diff --git a/docker-compose.yml b/docker-compose.yml index 2b3025c3..a4cfcd29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,11 +14,12 @@ services: - ./:/opt/www depends_on: - db + - cache deploy: resources: limits: - cpus: '0.3' - memory: '0.5GB' + cpus: '0.5' + memory: '1.0GB' api2: <<: *api @@ -38,8 +39,19 @@ services: deploy: resources: limits: - cpus: '0.7' - memory: '1.5GB' + cpus: '0.15' + memory: '0.4GB' + + cache: + container_name: rinha-de-backend-cache + image: redis:latest + ports: + - '6379:6379' + deploy: + resources: + limits: + cpus: '0.1' + memory: '0.5GB' nginx: image: nginx @@ -54,8 +66,8 @@ services: deploy: resources: limits: - cpus: '0.2' - memory: '0.5GB' + cpus: '0.25' + memory: '0.1GB' networks: default: diff --git a/migrations.sql b/migrations.sql index 284f260b..cde10ed0 100644 --- a/migrations.sql +++ b/migrations.sql @@ -1,7 +1,7 @@ create table person( - id binary(16) primary key, - nick varchar(32) not null, - name varchar(100) not null, - birth date not null, - stack json + id varchar(36) primary key unique, + nick varchar(32) not null, + name varchar(100) not null, + birth date not null, + stack json );