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

Design documentation for adding a raw-FFI thread manager #31

Open
wants to merge 42 commits into
base: main
Choose a base branch
from

Conversation

acarbonetto
Copy link

No description provided.

docs/design-raw-ffi.md Outdated Show resolved Hide resolved
docs/design-raw-ffi.md Show resolved Hide resolved
docs/design-raw-ffi.md Show resolved Hide resolved
docs/design-raw-ffi.md Outdated Show resolved Hide resolved
docs/design-raw-ffi.md Outdated Show resolved Hide resolved
docs/design-raw-ffi.md Outdated Show resolved Hide resolved
RustCore --> Redis
```

## Decision to use Raw-FFI calls directly to Rust-Core for Golang Wrapper

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move go to another doc too?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather hold off on splitting until we have an idea of where these documents will be stored.

docs/design-raw-ffi.md Show resolved Hide resolved
docs/design-raw-ffi.md Show resolved Hide resolved
docs/design-raw-ffi.md Outdated Show resolved Hide resolved
@Yury-Fridlyand
Copy link

I think you need to change base to an integration branch

Signed-off-by: acarbonetto <andrewc@bitquilltech.com>
Signed-off-by: acarbonetto <andrewc@bitquilltech.com>
Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Copy link
Author

@acarbonetto acarbonetto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathanl-bq we should also mention the routing API implemented in the protobuf object. ie sending to specific cluster nodes.

We should also describe what type of verification will be performed on the wrapper level. We should consider having a type list for each supported command, and return an error early is types are violated for any supposed command. The execute / executeRaw functions won't have any verification.

docs/design-api.md Outdated Show resolved Hide resolved
docs/design-api.md Show resolved Hide resolved
docs/design-api.md Outdated Show resolved Hide resolved
docs/design-api.md Outdated Show resolved Hide resolved
- There is additional overhead from marshalling data to and from protobuf, which could impact performance significantly

#### C Data Types
The wrapper language will pass commands, arguments, and configuration as C data types.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please take few examples of complex commands with many arguments, like sorted set or list for example https://lettuce.io/core/release/api/io/lettuce/core/api/sync/RedisSortedSetCommands.html.

Lets try to better undersrtand it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@asafpamzn also in the UDS solution we convert all passed arguments into a string array before passing it to the core. so there's no issue when passing complex commands.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to verify that all RESP3 value types can be returned using C data types

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment is addressed now, since we will starting with RESP2 for now.

Two different methods of sending requests will be supported.

### No Redis Type Validation
We will expose an `executeRaw` method that does no validation of the input types or command on the client side, leaving it up to Redis to reject the request should it be malformed. This gives the user the flexibility to send any type of request they want, including ones not officially supported yet.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add an example

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain what was the decision for type validation? There are two options

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided that we want to determine how much extra overhead protobuf adds to the product. Then compare with a custom payload in C. We can compare and share the solution for all raw-ffi solutions.

Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Transactions will be handled by adding a list of `Command`s to the protobuf request. The response will be a `redis::Value::Bulk`, which can be marshalled into a C array of values before being passed from Rust to the wrapper language. The wrapper language is responsible for converting the array of results to its own native collection type.

Pros:
- We get to reuse the protobuf definitions, meaning fewer files to update if we make changes to the protobuf definitions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Redis commands can be presented as a simple string array, so passing protobuf messages from the wrapper to the core adds unnecessary complication when we're talking about a raw FFI solution

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. we'll have a generic execute_command function in rust that excepts a string array and all FFI functions from the wrapper will call it

- There is additional overhead from marshalling data to and from protobuf, which could impact performance significantly

#### C Data Types
The wrapper language will pass commands, arguments, and configuration as C data types.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@asafpamzn also in the UDS solution we convert all passed arguments into a string array before passing it to the core. so there's no issue when passing complex commands.

- There is additional overhead from marshalling data to and from protobuf, which could impact performance significantly

#### C Data Types
The wrapper language will pass commands, arguments, and configuration as C data types.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to verify that all RESP3 value types can be returned using C data types


## API requirements:
- The API will be thread-safe.
- The API will accept as inputs all of [RESP2 types](https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md).
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a comment that we are planning on supporting RESP3 types when they are released.

docs/design-api.md Outdated Show resolved Hide resolved
docs/design-api.md Outdated Show resolved Hide resolved
docs/design-api.md Show resolved Hide resolved
docs/design-raw-ffi.md Show resolved Hide resolved
```

### With Redis Type Validation
We will expose separate methods for each supported command, and will attempt to validate the inputs for each of these methods. There will be a separate version of each method for transactions, as well as another version for Cluster Mode clients. We may leverage the compiler for the wrapper language to validate the types of the command arguments for statically typed languages. Since wrappers should be as lightweight as possible, we most likely will not be performing any checks for proper typing for non-statically typed languages.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"and will attempt to validate the inputs for each of these methods." -> the validation is done in the core/server itself

jonathanl-bq and others added 2 commits November 29, 2023 04:43
Co-authored-by: Andrew Carbonetto <andrewc@bitquilltech.com>
We will be supporting all Redis commands. Commands with higher usage will be prioritized, as determined by usage numbers from AWS ElastiCache usage logs.

## Type Validation
Two different methods of sending requests will be supported.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Two different methods of sending requests will be supported.
Two different methods of sending requests will be considered and compared, and we will go with the more performant solution.

Two different methods of sending requests will be supported.

### No Redis Type Validation
We will expose an `executeRaw` method that does no validation of the input types or command on the client side, leaving it up to Redis to reject the request should it be malformed. This gives the user the flexibility to send any type of request they want, including ones not officially supported yet.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided that we want to determine how much extra overhead protobuf adds to the product. Then compare with a custom payload in C. We can compare and share the solution for all raw-ffi solutions.

docs/design-api.md Outdated Show resolved Hide resolved
jonathanl-bq and others added 2 commits November 30, 2023 10:04
Co-authored-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Signed-off-by: Andrew Carbonetto <andrewc@bitquilltech.com>
Copy link

@Yury-Fridlyand Yury-Fridlyand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add index.md too

# Client Wrapper API design doc

## API requirements:
- The API will be thread-safe.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be use present instead of future?

## Command Interface

### Unix Domain Socket solution
For clients based on Unix Domain Sockets (UDS), we will use the existing Rust-core protobuf messages for creating a connection, sending requests, and receiving responses. Supported commands are enumerated in the [protobuf definition for requests](../babushka-core/src/protobuf/redis_request.proto) and we will support them as they are added in the future. Note: the `CustomCommand` request type is also adequate for all commands. As defined in the [protobuf definition for responses](../babushka-core/src/protobuf/response.proto), client wrappers will receive data as a pointer, which can be passed to Rust to marshal the data back into the wrapper language’s native data types. In the future, we plan on optimizing this approach such that small data is returned as an object and large data is returned as a pointer in order to reduce the overhead of FFI calls in the case of small data.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reword the first sentense - UDS is a transport, not a base for a client

```

## Routing Options
We will be supporting routing Redis requests to all nodes, all primary nodes, or a random node. For more specific routing to a node, we will also allow sending a request to a primary or replica node with a specified hash slot or key. When the wrapper given a key route, the key is passed to the Rust core, which will find the corresponding hash slot for it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will

or

We should

or

The client should/will/etc

Also: future or present? (here and below)

### Explicitly Supported Command
We will expose separate methods for each supported command. There will be a separate version of each method for transactions, as well as another version for Cluster Mode clients. For statically typed languages, we will leverage the compiler of the wrapper language to validate the types of the command arguments as much as possible. Since wrappers should be as lightweight as possible, we will be performing very few to no checks for proper typing for non-statically typed languages.

## Errors

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reference where these errors came from

We will expose separate methods for each supported command. There will be a separate version of each method for transactions, as well as another version for Cluster Mode clients. For statically typed languages, we will leverage the compiler of the wrapper language to validate the types of the command arguments as much as possible. Since wrappers should be as lightweight as possible, we will be performing very few to no checks for proper typing for non-statically typed languages.

## Errors
ClosingError: Errors that report that the client has closed and is no longer usable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ClosingError: Errors that report that the client has closed and is no longer usable.
`ClosingError`: Errors that report that the client has closed and is no longer usable.

And below

// submit three commands in a single transaction to Redis
Command getApplesRequest = Command.builder()
.requestType(GETSTRING)
.arguments(new String[]{apples"})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.arguments(new String[]{apples"})
.arguments(new String[]{"apples"})

@@ -0,0 +1,240 @@
# Babushka Core Wrappers

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename all references

Suggested change
# Babushka Core Wrappers
# Glide Core Wrappers

activate Wrapper
activate Client
Wrapper -)+ ffi: connect_to_redis
ffi -)+ manager: start_socket_listener(init_callback)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ffi -)+ manager: start_socket_listener(init_callback)
ffi -)+ manager: start_socket_listener

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename file - it shows both raw FFI and UDS approaches

Client -->> SocketListener: ClientUsageResult
SocketListener ->> Socket: write_result
Socket -->> SocketListener:
Wrapper ->> Socket: netty.read (protobuf.response)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not correct. May be we need to split diagram into 2 or 3 ones: java to UDS, UDS to rust and java-uds-rust in zoom out mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants