0

I am setting up a c server and used tarantool as databased using tarantool-c. However, everytime I setup read_reply() the request per second tanks so much its like using mysql. How to fix it?

Jones G
  • 301
  • 5
  • 17
  • When you read reply after each request, you switch to synchronous mode from asynchronous. You should read reply when it's ready, tarantool-c allows programming in both synchronous and asynchronous fashion. – Kostja Jan 15 '19 at 08:04
  • I can do async, but i'm worried that the reply does not go to which ever it will go. Say for example one `tnt_select()` from one request, then a `tnt_delete()` from another request, then a third one doing a different `tnt_select()`. Suppose there were sent to the same network buffer at the same time. – Jones G Jan 15 '19 at 15:20
  • Now, supposed tarantool sent a reply but on different times, since each query are different in difficulty, Am I assured that the reply goes to `tnt_select` or `tnt_delete` that sent the `tnt_flush()` for this and that query. – Jones G Jan 15 '19 at 15:24
  • Or I might be wrong? Probably `read_reply` attaches an `id` to the queries so that when tarantool return the result, `ready_reply` will search for that `id` in the buffer and says something like, "oh It got my ID this one is mine"? – Jones G Jan 15 '19 at 15:29

1 Answers1

1

We had the discussion with James and he shares the code. The code implements the http server and this is how it processes a request:

  • Accept a new incoming http request.
  • Send a request to tarantool (using binary protocol).
  • Wait for a reply from tarantool (synchronously, unable to handle other incoming http requests).
  • Answer to the http request.

The root of the problem here is that we unable to utilize full network bandwith between the http server and tarantool. Such server should use select() / poll() / epoll() (on Linux) / kqueue (on FreeBSD) or a library like libev to determine whether it is able to write to a socket, read from it or accept a request.

Let me describe in brief how it should operate to at least utilize a network as much as possible (when doing it from one thread) in the set of rules of when-X-then-Y kind:

  • When a new http request arrive it should register the need to send a request to tarantool (let me name it pending request).
  • When a socket to tarantool is ready for write and there is at least one pending request the server should form a request to tarantool, save its ID (see tnt_stream.reqid) and write the request to the socket.
  • When the socket to tarantool is ready to read the server should read a reply from tarantool, match its ID (see tnt_reply.sync) against saved one and write a response to an http client, then close the socket to the client.

If http keepalive / pipelining need to be supported, the server needs to check a socket to an http client for readiness to perform read() / write(). Also it need to register pending http responses rather then write it just when they appears.

Aside of that HTTP itself is not easy to implement in a proper way: it always give you surprises if you don't control implementations of both client and server.

So I will describe alternatives to implementing its own http server, that are compatible with tarantool and able to operate with it in asynchronous way to achieve good performance. They are:

  • Using http.server tarantool module that allows to process http requests right in tarantool w/o external service and w/o using a connector to tarantool.
  • Using nginx_upstream_tarantool nginx module that allows to perform a request to tarantool from nginx using the binary protocol.

There are cons and pros for both of these ways. However http.server disadvantages can be overcomed with using nginx as frontend to proxying requests to tarantool(s) saving http.server advantages.

http.server cons:

  • No https support.
  • Single CPU bound / single tarantool instance bound.
  • Possibly worse performance then nginx (but not much).
  • Possibly worse support of HTTP caveats (but I don't know one).

http.server pros:

  • Simpler to start developing.
  • Simpler in configuration / deployment: parts of an application configuration do not spread across configs for tarantool and nginx.

nginx_upstream_tarantool cons and pros are reverse of http.server ones. Also I would mention specifically that nginx allows you to balance load across several tarantool instances, which may form a replication group or may be sharding frontends. This ability can be used to scale a service in the sense of desired performance as with proxying to http.server as well as with nginx_upstream_tarantool.

I guess you also interested in benchmarking results for http.server and nginx_upstream_tarantool. Look at this measurement. Note however that it is quite synthetic: it performs small requests and answer with small responses. Real RPS numbers may be different depending of size of requests and responses, hardware, whether https is needed, etc.

  • Nice explained but I am no lucky to see amazing "500K-1000K transactions per second on one CPU core" like in this post https://gist.github.com/danikin/a5ddc6fe0cedc6257853 I am currently using Rust project web framework called: Actix Web https://github.com/zheludkovm/RustyTarantool/blob/master/examples/simple-bench.rs The benchmark could handle only around 5k rps. I am new on Tarantool help advise me correct way – Meas Aug 01 '22 at 20:04
  • For historical purposes: we had a coversation with Meas [here](https://github.com/tarantool/tarantool/discussions/7506) and, it seems, found the problem in his code. – Alexander Turenko Aug 04 '22 at 20:23