Skip to content

Commit

Permalink
Docs: add a design document for the Client
Browse files Browse the repository at this point in the history
Add a document that states the requirements for the Client, and then
proposes a design that fulfills them.
  • Loading branch information
CuriousGeorgiy committed Dec 28, 2023
1 parent ac98986 commit 7e4ac2d
Showing 1 changed file with 184 additions and 0 deletions.
184 changes: 184 additions & 0 deletions docs/client-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Tntcxx Client Design

## Scope

This document describes the design of the Tntcxx Client. First we state the
requirements and use cases we have in mind, and then we propose a design that
fulfills them.

## Requirements

### Functional Requirements

We envision Tntcxx to be used primarily as a part of network applications (e.g.,
an HTTP server), the Tntcxx Client backing requests to Tarantool. Such
applications are usually built around a central event processing loop, which is
responsible for multiplexing network I/O.

#### Application Event Processing Loop Integration

The first and foremost requirement is that there must be a convenient
way to integrate the Tntcxx Client into the event processing loop used by the
application. Moreover, the Tntcxx Client must never run the event processing
loop itself.

At the same time, since event processing loops are inherently single threaded,
we do expect the Tntcxx Client to be used in a multithreaded environment, i.e.,
when connections and reqeust futures are in different threads. So we do not aim
to design the Tntcxx Client to be thread-safe.

#### Asynchronous Request Processing

Since the Tntcxx Client is constrained from running the event processing loop,
the Tntcxx Client must support asynchronous request processing through
application-provided callbacks, futures or scatter-gather. The Tntcxx Client
must only be responsible for posting requests — the responses must be processed
and returned to the application as they arrive, i.e., asynchronously.

#### Connection Status

The application must be able to check the status of a Tntcxx Client.

#### Request Status

The application must be able to check the status of a Tntcxx Client request.

#### Request Cancelling

The application must be able to cancel a Tntcxx Client request.

#### Error Handling

There must be a convenient exception-safe way for the application to handle
errors arising throughout the Tntcxx Client request lifecycle. A request error
returned either through the callback, or through the future.

#### Request Handling

In order for the application to be able to manage a request, a request handle is
always returned. The application can check the request status, cancel the
request, handle request errors and retrieve the response through this handle
(only once). However, if the response was retrieved by other means (either
returned through a callback or collected through scatter-gather), the handle
cannot return the response.

#### Request Timeout

Since the Tntcxx Client does not have control over the application's event
processing loop, the application must implement its own request timeouts. The
application can cancel stale requests.

#### Response Lifetime

The response lifetime is managed implicitly and does not require any
interference from the application. The response can be retrieved only once, and
it is uniquely owned by the application (i.e., it is not copyable).

#### Request Retrying

Since the Tntcxx Client does not have control over the application's event
processing loop, the application must implement its own request retrying.

#### Reconnection

The Tntcxx Client must support implicit reconnection with the same session
settings it was created with.

#### Connection Pool

TBD.

#### Transactions

TBD.

#### Failover

TBD.

## Design

### Socket

```c++
template<class Resource>
class Socket {
public:
Resource get_resource();
};
```
### Network Provider
```c++
template<class Socket, class Data *>
class NetProvider {
public:
template<class Socket, class Data *>
using read_ready_cb_f = void (*)(Socket sock, Data *data);
template<class Socket, class Data *>
using write_ready_cb_f = void (*)(Socket sock, Data *data);
int register(Socket sock, Data *data, read_ready_cb_f read_ready_cb, write_ready_cb_f write_ready_cb);
int unregister(Socket sock);
};
```

```c++
template<class Socket, class Data *>
class EpollNetProviderData {
public:
EpollNetProviderData(Socket sock, Data *data, read_ready_cb_f read_ready_cb, write_ready_cb_f write_ready_cb);

int type;

void read_ready();
void write_ready();
};
```
### Connection
```c++
struct ConnectionOptions {
/* All existing options. */
static constexpr size_t DEFAULT_RECONNECTION_INTERVAL = 2;
size_t reconnection_interval = DEFAULT_RECONNECTION_INTERVAL;
};
template<class NetProvider>
class Connection {
public:
using request_cb_f = void (*)(std::optional<RequestError> error, Response response, Connection &connection);
Connection(NetProvider net_provider, ConnectionOptions connection_options);
enum class ConnectionStatus status();
std::optional<ConnectionError> error();
/* An abstract request's interface. */
Request some_request(/* options */);
Request some_request(/* options */, request_cb_f request_cb);
template<class Container>
void gather_responses(Container<Response> &responses);
};
```

### Request

```c++
class Request {
public:
enum class RequestStatus status();

std::optional<RequestError> error();

void cancel();

std::optional<Response> get_response();
};
```

0 comments on commit 7e4ac2d

Please sign in to comment.