diff options
Diffstat (limited to 'docs/beep-beep/kv.md')
-rw-r--r-- | docs/beep-beep/kv.md | 679 |
1 files changed, 0 insertions, 679 deletions
diff --git a/docs/beep-beep/kv.md b/docs/beep-beep/kv.md deleted file mode 100644 index 0ea41034..00000000 --- a/docs/beep-beep/kv.md +++ /dev/null @@ -1,679 +0,0 @@ -# KV (Key-Value) Plugin - -The Key-Value plugin provides the ability to store arbitrary data inside the -RoadRunner between different requests (in case of HTTP application) or different -types of applications. Thus, using [Temporal](https://docs.temporal.io/docs/php/introduction), -for example, you can transfer data inside the [HTTP application](/php/worker.md) -and vice versa. - -As a permanent source of data, the RoadRunner allows you to use popular solutions, -such as [Redis Server](https://redis.io/) or [Memcached](https://memcached.org/), -but in addition it provides others that do not require a separate server, such -as [BoltDB](https://github.com/boltdb/bolt), and also allows you to replace -permanent storage with temporary that stores data in RAM. - -![kv-general-info](https://user-images.githubusercontent.com/2461257/128436785-3dadbf0d-13c3-4e0c-859c-4fd9668558c8.png) - -## Installation - -> **Requirements** -> - PHP >= 7.4 -> - RoadRunner >= 2.3 -> - *ext-protobuf (optional)* - -To get access from the PHP code, you should put the corresponding dependency -using [the Composer](https://getcomposer.org/). - -```sh -$ composer require spiral/roadrunner-kv -``` - -## Configuration - -After installing all the required dependencies, you need to configure this -plugin. To enable it add `kv` section to your configuration: - -```yaml -rpc: - listen: tcp://127.0.0.1:6001 - -kv: - example: - driver: memory -``` - -Please note that to interact with the KV, you will also need the RPC defined -in `rpc` configuration section. You can read more about the configuration and -methods of creating the RPC connection on the [documentation page here](/beep-beep/rpc.md). - -This configuration initializes this plugin with one storage named "`example`". -In addition, each storage must have a `driver` that indicates the type of -connection used by those storages. In total, at the moment, 4 different types of -drivers are available with their own characteristics and additional settings: -`boltdb`, `redis`, `memcached` and `memory`. - -The `memory` and `boltdb` drivers do not require additional binaries and are -available immediately, while the rest require additional setup. Please see -the appropriate documentation for installing [Redis Server](https://redis.io/) -and/or [Memcached Server](https://memcached.org/). - -### Memory Driver Configuration - -This type of driver is already supported by the RoadRunner and does not require -any additional installations. - -Please note that using this type of storage, all -data is contained in memory and will be destroyed when the RoadRunner Server -is restarted. If you need persistent storage without additional dependencies, -then it is recommended to use the boltdb driver. - -The complete memory driver configuration looks like this: - -```yaml -kv: - # User defined name of the storage. - memory: - # Required section. - # Should be "memory" for the memory driver. - driver: memory - - # Optional section. - # Default: 60 - interval: 60 -``` - -Below is a more detailed description of each of the memory-specific options: - -- `interval` - The interval (in seconds) between checks for the lifetime of the - value in the cache. For large values of the interval, the cache item will be - checked less often for expiration of its lifetime. It is recommended to use - large values only in cases when the cache is used without expiration values, - or in cases when this value is not critical to the architecture of your - application. Note that the lower this value, the higher the load on the - system. - -### Boltdb Driver Configuration - -This type of driver is already supported by the RoadRunner and does not require -any additional installations. - -The complete boltdb driver configuration looks like this: - -```yaml -kv: - # User defined name of the storage. - boltdb: - # Required section. - # Should be "boltdb" for the boltdb driver. - driver: boltdb - - # Optional section. - # Default: "rr.db" - file: "rr.db" - - # Optional section. - # Default: "." - dir: "." - - # Optional section. - # Default: 0777 - permissions: 0777 - - # Optional section. - # Default: "rr" - bucket: "rr" - - # Optional section. - # Default: 60 - interval: 60 -``` - -Below is a more detailed description of each of the boltdb-specific options: - -- `file` - Database file pathname name. In the case that such a file does not - exist in, RoadRunner will create this file on its own at startup. Note that this - must be an existing directory, otherwise a "The system cannot find the path - specified" error will be occurred, indicating that the full database pathname is - invalid. - -- `dir` - The directory prefix string where the database file will be located. - When forming the path to the file, this prefix will be added before the pathname - defined in `file` section. - -- `permissions` - The file permissions in UNIX format of the database file, set - at the time of its creation. If the file already exists, the permissions will - not be changed. - -- `bucket` - The bucket name. You can create several boltdb connections by - specifying different buckets and in this case the data stored in one bucket will - not intersect with the data stored in the other, even if the database file and - other settings are completely identical. - -- `interval` - The interval (in seconds) between checks for the lifetime of the - value in the cache. The meaning and behavior is similar to that used in the - case of the memory driver. - - -### Redis Driver Configuration - -Before configuring the Redis driver, please make sure that the Redis Server is -installed and running. You can read more about this [in the documentation](https://redis.io/). - -In the simplest case, when a full-fledged cluster or a fault-tolerant system is -not required, we have one connection to the Redis Server. The configuration of -such a connection will look like this. - -```yaml -kv: - # User defined name of the storage. - redis: - # Required section. - # Should be "redis" for the redis driver. - driver: redis - - # Optional section. - # By default, one connection will be specified with the - # "localhost:6379" value. - addrs: - - "localhost:6379" - - # Optional section. - # Default: "" - username: "" - - # Optional section. - # Default: "" - password: "" - - # Optional section. - # Default: 0 - db: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 5 seconds) - dial_timeout: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 3 retries) - max_retries: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 8ms) - min_retry_backoff: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 512ms) - max_retry_backoff: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 10 connections per CPU). - pool_size: 0 - - # Optional section. - # Default: 0 (do not use idle connections) - min_idle_conns: 0 - - # Optional section. - # Default: 0 (do not close aged connections) - max_conn_age: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 3s) - read_timeout: 0 - - # Optional section. - # Default: 0 (equivalent to the value specified in the "read_timeout" section) - write_timeout: 0 - - # Optional section. - # Default: 0 (equivalent to the value specified in the "read_timeout" + 1s) - pool_timeout: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 5m) - idle_timeout: 0 - - # Optional section. - # Default: 0 (equivalent to the default value of 1m) - idle_check_freq: 0 - - # Optional section. - # Default: false - read_only: false -``` - -Below is a more detailed description of each of the Redis-specific options: - -- `addrs` - An array of strings of connections to the Redis Server. Must - contain at least one value of an existing connection in the format of host or - IP address and port, separated by a colon (`:`) character. - -- `username` - Optional value containing the username credentials of the Redis - connection. You can omit this field, or specify an empty string if the - username of the connection is not specified. - -- `password` - Optional value containing the password credentials of the Redis - connection. You can omit this field, or specify an empty string if the - password of the connection is not specified. - -- `db` - An optional identifier for the database used in this connection to the - Redis Server. Read more about databases section on the documentation page for - the description of the [select command](https://redis.io/commands/select). - -- `dial_timeout` - Server connection timeout. A value of `0` is equivalent to a - timeout of 5 seconds (`5s`). After the specified time has elapsed, if the - connection has not been established, a connection error will occur. - - Must be in the format of a "numeric value" + "time format suffix", like "`2h`" where - suffixes means: - - `h` - the number of hours. For example `1h` means 1 hour. - - `m` - the number of minutes. For example `2m` means 2 minutes. - - `s` - the number of seconds. For example `3s` means 3 seconds. - - `ms` - the number of milliseconds. For example `4ms` means 4 milliseconds. - - If no suffix is specified, the value will be interpreted as specified in - nanoseconds. In most cases, this accuracy is redundant and may not be true. - For example `5` means 5 nanoseconds. - - Please note that all time intervals can be suffixed. - -- `max_retries` - Maximum number of retries before giving up. Specifying `0` is - equivalent to the default (`3` attempts). If you need to specify an infinite - number of connection attempts, specify the value `-1`. - -- `min_retry_backoff` - Minimum backoff between each retry. Must be in the format - of a "numeric value" + "time format suffix". A value of `0` is equivalent to a - timeout of 8 milliseconds (`8ms`). A value of `-1` disables backoff. - -- `max_retry_backoff` - Maximum backoff between each retry. Must be in the format - of a "numeric value" + "time format suffix". A value of `0` is equivalent to a - timeout of 512 milliseconds (`512ms`). A value of `-1` disables backoff. - -- `pool_size` - Maximum number of RoadRunner socket connections. A value of `0` - is equivalent to a `10` connections per every CPU. Please note that specifying - the value corresponds to the number of connections **per core**, so if you - have 8 cores in your system, then setting the option to 2 you will get 16 - connections. - -- `min_idle_conns` - Minimum number of idle connections which is useful when - establishing new connection is slow. A value of 0 means no such idle - connections. More details about the problem requiring the presence of this - option available in the [corresponding issue](https://github.com/go-redis/redis/issues/772). - -- `max_conn_age` - Connection age at which client retires (closes) the connection. - A value of `0` is equivalent to a disabling this option. In this case, aged - connections will not be closed. - -- `read_timeout` - Timeout for socket reads. If reached, commands will fail with - a timeout instead of blocking. Must be in the format of a "numeric value" + - "time format suffix". A value of `0` is equivalent to a timeout of 3 seconds - (`3s`). A value of `-1` disables timeout. - -- `write_timeout` - Timeout for socket writes. If reached, commands will fail - with a timeout instead of blocking. A value of `0` is equivalent of the value - specified in the `read_timeout` section. If `read_timeout` value is not - specified, a value of 3 seconds (`3s`) will be used. - -- `pool_timeout` - Amount of time client waits for connection if all connections - are busy before returning an error. A value of `0` is equivalent of the value - specified in the `read_timeout` + `1s`. If `read_timeout` value is not - specified, a value of 4 seconds (`4s`) will be used. - -- `idle_timeout` - Amount of time after which client closes idle connections. - Must be in the format of a "numeric value" + "time format suffix". A value of - `0` is equivalent to a timeout of 5 minutes (`5m`). A value of `-1` disables - idle timeout check. - -- `idle_check_freq` - Frequency of idle checks made by idle connections reaper. - Must be in the format of a "numeric value" + "time format suffix". A value of - `0` is equivalent to a timeout of 1 minute (`1m`). A value of `-1` disables - idle connections reaper. Note, that idle connections are still discarded by - the client if `idle_timeout` is set. - -- `read_only` - An optional boolean value that enables or disables read-only - mode. If `true` value is specified, the writing will be unavailable. - Note that this option **not allowed** when working with Redis Sentinel. - -These are all options available for all Redis connection types. - -#### Redis Cluster - -In the case that you want to configure a [Redis Cluster](https://redis.io/topics/cluster-tutorial), -then you can specify additional options required only if you are organizing this -type of server. - -When creating a cluster, multiple connections are available to you. For example, -you call such a command `redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380`, -you should specify the appropriate set of connections. In addition, when -organizing a cluster, two additional options with algorithms for working with -connections will be available to you: `route_by_latency` and `route_randomly`. - -```yaml -kv: - redis: - driver: redis - addrs: - - "127.0.0.1:6379" - - "127.0.0.1:6380" - - # Optional section. - # Default: false - route_by_latency: false - - # Optional section. - # Default: false - route_randomly: false -``` - -Where new options means: - -- `route_by_latency` - Allows routing read-only commands to the closest master - or slave node. If this option is specified, the `read_only` configuration value - will be automatically set to `true`. - -- `route_randomly` - Allows routing read-only commands to the random master or - slave node. If this option is specified, the `read_only` configuration value - will be automatically set to `true`. - -#### Redis Sentinel - -Redis Sentinel provides high availability for Redis. You can find more -information about [Sentinel on the documentation page](https://redis.io/topics/sentinel). - -There are two additional options available for the Sentinel configuration: -`master_name` and `sentinel_password`. - -```yaml -kv: - redis: - driver: redis - - # Required section. - master_name: "" - - # Optional section. - # Default: "" (no password) - sentinel_password: "" -``` - -Where Sentinel's options means: - -- `master_name` - The name of the Sentinel's master in string format. - -- `sentinel_password` - Sentinel password from "requirepass <password>" - (if enabled) in Sentinel configuration. - - -### Memcached Driver Configuration - -Before configuring the Memcached driver, please make sure that the Memcached -Server is installed and running. You can read more about this [in the documentation](https://memcached.org/). - -The complete memcached driver configuration looks like this: - -```yaml -kv: - # User defined name of the storage. - memcached: - # Required section. - # Should be "memcached" for the memcached driver. - driver: memcached - - # Optional section. - # Default: "localhost:11211" - addr: "localhost:11211" -``` - -Below is a more detailed description of each of the memcached-specific options: - -- `addr` - String of memcached connection in format "`[HOST]:[PORT]`". In the case - that there are several memcached servers, then the list of connections can be - listed in an array format, for example: `addr: [ "localhost:11211", "localhost:11222" ]`. - -## Usage - -First, you need to create the RPC connection to the RoadRunner server. You can -specify an address with a connection by hands or use automatic detection if -you run the php code as a [RoadRunner Worker](/php/worker.md). - -```php -use Spiral\RoadRunner\Environment; -use Spiral\Goridge\RPC\RPC; - -// Manual configuration -$rpc = RPC::create('tcp://127.0.0.1:6001'); - -// Autodetection -$env = Environment::fromGlobals(); -$rpc = RPC::create($env->getRPCAddress()); -``` - -After creating the RPC connection, you should create the -`Spiral\RoadRunner\KeyValue\Factory` object for working with storages of KV -RoadRunner plugin. - -The factory object provides two methods for working with the plugin. - -- Method `Factory::isAvailable(): bool` returns boolean `true` value if the - plugin is available and `false` otherwise. - -- Method `Factory::select(string): CacheInterface` receives the name of the - storage as the first argument and returns the implementation of the - [PSR-16](https://www.php-fig.org/psr/psr-16/) `Psr\SimpleCache\CacheInterface` - for interact with the key-value RoadRunner storage. - -```php -use Spiral\Goridge\RPC\RPC; -use Spiral\RoadRunner\KeyValue\Factory; - -$factory = new Factory(RPC::create('tcp://127.0.0.1:6001')); - -if (!$factory->isAvailable()) { - throw new \LogicException('The "kv" RoadRunner plugin not available'); -} - -$storage = $factory->select('storage-name'); -// Expected: -// An instance of Psr\SimpleCache\CacheInterface interface - -$storage->set('key', 'value'); - -echo $storage->get('key'); -// Expected: -// string(5) "string" -``` - -> The `clear()` method available since [RoadRunner v2.3.1](https://github.com/spiral/roadrunner/releases/tag/v2.3.1). - -Apart from this, RoadRunner Key-Value API provides several additional methods: -You can use `getTtl(string): ?\DateTimeInterface` and -`getMultipleTtl(string): iterable<\DateTimeInterface|null>` methods to get -information about the expiration of an item stored in a key-value storage. - -> Please note that the `memcached` driver -> [**does not support**](https://github.com/memcached/memcached/issues/239) -> these methods. - -```php -$ttl = $factory->select('memory') - ->getTtl('key'); -// Expected: -// - An instance of \DateTimeInterface if "key" expiration time is available -// - Or null otherwise - -$ttl = $factory->select('memcached') - ->getTtl('key'); -// Expected: -// Spiral\RoadRunner\KeyValue\Exception\KeyValueException: Storage "memcached" -// does not support kv.TTL RPC method execution. Please use another driver for -// the storage if you require this functionality. -``` - -### Value Serialization - -To save and receive data from the key-value store, the data serialization -mechanism is used. This way you can store and receive arbitrary serializable -objects. - -```php -$storage->set('test', (object)['key' => 'value']); - -$item = $storage->set('test'); -// Expected: -// object(StdClass)#399 (1) { -// ["key"] => string(5) "value" -// } -``` - -To specify your custom serializer, you will need to specify it in the key-value -factory constructor as a second argument, or use the -`Factory::withSerializer(SerializerInterface): self` method. - -```php -use Spiral\Goridge\RPC\RPC; -use Spiral\RoadRunner\KeyValue\Factory; - -$connection = RPC::create('tcp://127.0.0.1:6001'); - -$storage = (new Factory($connection)) - ->withSerializer(new CustomSerializer()) - ->select('storage'); -``` - -In the case that you need a specific serializer for a specific value from the -storage, then you can use a similar method `withSerializer()` for a specific -storage. - -```php -// Using default serializer -$storage->set('key', 'value'); - -// Using custom serializer -$storage - ->withSerializer(new CustomSerializer()) - ->set('key', 'value'); -``` - - -#### Igbinary Value Serialization - -As you know, the serialization mechanism in PHP is not always productive. To -increase the speed of work, it is recommended to use the -[ignbinary extension](https://github.com/igbinary/igbinary). - -- For the Windows OS, you can download it from the - [PECL website](https://windows.php.net/downloads/pecl/releases/igbinary/). - -- In a Linux and MacOS environment, it may be installed with a simple command: -```sh -$ pecl install igbinary -``` - -More detailed installation instructions are [available here](https://github.com/igbinary/igbinary#installing). - -After installing the extension, you just need to install the desired igbinary -serializer in the factory instance. - -```php -use Spiral\Goridge\RPC\RPC; -use Spiral\RoadRunner\KeyValue\Factory; -use Spiral\RoadRunner\KeyValue\Serializer\IgbinarySerializer; - -$storage = (new Factory(RPC::create('tcp://127.0.0.1:6001'))) - ->withSerializer(new IgbinarySerializer()) - ->select('storage'); -// -// Now this $storage is using igbinary serializer. -// -``` - -#### End-to-End Value Encryption - -Some data may contain sensitive information, such as personal data of the user. -In these cases, it is recommended to use data encryption. - -To use encryption, you need to install the -[Sodium extension](https://www.php.net/manual/en/book.sodium.php). - -Next, you should have an encryption key generated using -[sodium_crypto_box_keypair()](https://www.php.net/manual/en/function.sodium-crypto-box-keypair.php) -function. You can do this using the following command: -```sh -$ php -r "echo sodium_crypto_box_keypair();" > keypair.key -``` - -> Do not store security keys in a control versioning systems (like GIT)! - -After generating the keypair, you can use it to encrypt and decrypt the data. - -```php -use Spiral\Goridge\RPC\RPC; -use Spiral\RoadRunner\KeyValue\Factory; -use Spiral\RoadRunner\KeyValue\Serializer\SodiumSerializer; -use Spiral\RoadRunner\KeyValue\Serializer\DefaultSerializer; - -$storage = new Factory(RPC::create('tcp://127.0.0.1:6001')); - ->select('storage'); - -// Encrypted serializer -$key = file_get_contents(__DIR__ . '/path/to/keypair.key'); -$encrypted = new SodiumSerializer($storage->getSerializer(), $key); - -// Storing public data -$storage->set('user.login', 'test'); - -// Storing private data -$storage->withSerializer($encrypted) - ->set('user.email', '[email protected]'); -``` - -## RPC Interface - -All communication between PHP and GO made by the RPC calls with protobuf payloads. -You can find versioned proto-payloads here: [Proto](https://github.com/spiral/roadrunner/tree/v2.3.0/pkg/proto/kv/v1beta). - -- `Has(in *kvv1.Request, out *kvv1.Response)` - The arguments: the first argument -is a `Request` , which declares a `storage` and an array of `Items` ; the second -argument is a `Response`, it will contain `Items` with keys which are present in -the provided via `Request` storage. Item value and timeout are not present in -the response. The error returned if the request fails. - -- `Set(in *kvv1.Request, _ *kvv1.Response)` - The arguments: the first argument -is a `Request` with the `Items` to set; return value isn't used and present here -only because GO's RPC calling convention. The error returned if request fails. - -- `MGet(in *kvv1.Request, out *kvv1.Response)` - The arguments: the first -argument is a `Request` with `Items` which should contain only keys (server -doesn't check other fields); the second argument is `Response` with the `Items`. -Every item will have `key` and `value` set, but without timeout (See: `TTL`). -The error returned if request fails. - -- `MExpire(in *kvv1.Request, _ *kvv1.Response)` - The arguments: the first -argument is a `Request` with `Items` which should contain keys and timeouts set; -return value isn't used and present here only because GO's RPC calling convention. -The error returned if request fails. - -- `TTL(in *kvv1.Request, out *kvv1.Response)` - The arguments: the first argument -is a `Request` with `Items` which should contain keys; return value will contain -keys with their timeouts. The error returned if request fails. - -- `Delete(in *kvv1.Request, _ *kvv1.Response)` - The arguments: the first -argument is a `Request` with `Items` which should contain keys to delete; return -value isn't used and present here only because GO's RPC calling convention. -The error returned if request fails. - -- `Clear(in *kvv1.Request, _ *kvv1.Response)` - The arguments: the first -argument is a `Request` with `storage` which should contain the storage to be -cleaned up; return value isn't used and present here only because GO's RPC -calling convention. The error returned if request fails. - -From the PHP point of view, such requests (`MGet` for example) are as follows: -```php -use Spiral\Goridge\RPC\RPC; -use Spiral\Goridge\RPC\Codec\ProtobufCodec; -use Spiral\RoadRunner\KeyValue\DTO\V1\{Request, Response}; - -$response = RPC::create('tcp://127.0.0.1:6001') - ->withServicePrefix('kv') - ->withCodec(new ProtobufCodec()) - ->call('MGet', new Request([ ... ]), Response::class); -``` |