# Thrift Eio Flow transport - suggestions?

**URL:** https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385
**Category:** Ecosystem
**Tags:** eio
**Created:** [June 13, 2023, 3:10am UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385 "2023-06-13T03:10:39Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)
#### Post date: [June 13, 2023, 3:10am UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/1 "2023-06-13T03:10:39Z")

</div>

I am exploring using Thrift with Eio. Currently, the Thrift support library for OCaml uses plain old Unix sockets. But Thrift is fairly flexible in how the low-level networking is done, and abstracts away the details behind a [‘transport’ interface](https://github.com/vbmithr/ocaml-thrift-lib/blob/1e5b7924636d8926114cbd34398a0848cd472e83/src/Thrift.ml#L42). The interface however does not use async I/O types, so before Eio it was not possible to adapt it to use Lwt:

```auto
class virtual t =
  object (self)
    method virtual isOpen : bool
    method virtual opn : unit
    method virtual close : unit
    method virtual read : bytes -> int -> int -> int
    method readAll (buf:bytes) off len = (* this is implemented *)
    method virtual write : bytes -> int -> int -> unit
    method virtual write_string : string -> int -> int -> unit
    method virtual flush : unit
  end

```

Thanks to Eio though, we can implement this virtual class to use a flow:

```auto
(* TFlowTransport.ml *)

open Thrift
module T = Transport

class t flow = object
  val mutable opened = true
  inherit Transport.t

  method isOpen = opened
  method opn = ()

  method close =
    Eio.Net.close flow;
    opened <- false

  method read buf off len =
    let cstruct = Cstruct.create len in
    try
      Eio.Flow.read_exact flow cstruct;
      Cstruct.blit_to_bytes cstruct 0 buf off len;
      len
    with
    | End_of_file ->
      raise (T.E (T.END_OF_FILE, (Printf.sprintf "TSocket: Could not read %d because: end of file" len)))
    | _ -> raise (T.E (T.UNKNOWN, (Printf.sprintf "TSocket: Could not read %d" len)))

  method write buf off len =
    let cstruct = Cstruct.of_bytes ~off ~len buf in
    Eio.Flow.write flow [cstruct]

  method write_string buf off len =
    let cstruct = Cstruct.of_string ~off ~len buf in
    Eio.Flow.write flow [cstruct]

  method flush = ()
end

```

But as you can see above, reading from/writing to the flow is a little clumsy because flows don’t work directly in terms of byte buffers with offsets and lengths, they work in terms of cstructs. Can anyone advise whether I am missing something or if allocating cstructs is really the way to implement these methods using Eio?

---

<div class="post-metadata">

### Author: ![patricoferris](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/patricoferris/32/3621_2.png) [@patricoferris](https://discuss.ocaml.org/u/patricoferris)
#### Post date: [June 13, 2023, 10:37am UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/2 "2023-06-13T10:37:17Z")

</div>

I don’t think there’s much you can do at the moment, but see this issue for a discussion on Cstruct vs. Bytes [Decide on cstruct vs bytes · Issue #140 · ocaml-multicore/eio · GitHub](https://github.com/ocaml-multicore/eio/issues/140). I think there’s a desire to move to bytes if the GC-story works out nicely, but I’m not an expert on that.

---

<div class="post-metadata">

### Author: ![lambda\_foo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lambda_foo/32/275_2.png) [@lambda\_foo](https://discuss.ocaml.org/u/lambda_foo)
#### Post date: [June 13, 2023, 1:33pm UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/3 "2023-06-13T13:33:54Z")

</div>

> [@yawaramin](#):
>
> I am exploring using Thrift with Eio.

Unrelated to the EIO usage, what is the reason for wanting to use Thrift compared to something else?

---

<div class="post-metadata">

### Author: ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)
#### Post date: [June 13, 2023, 4:14pm UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/4 "2023-06-13T16:14:44Z")

</div>

I have existing Thrift services running in production and I want to interact with them/extend them.

However I think Thrift compares pretty favourably to many other options–supports a large number of languages, solid story for schema evolution, elegant and decoupled design. Sure, it has some warts and performance-wise maybe not at the top. But with RPC systems the thing is that nobody is totally satisfied with any of them 🙂

---

<div class="post-metadata">

### Author: ![lambda\_foo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lambda_foo/32/275_2.png) [@lambda\_foo](https://discuss.ocaml.org/u/lambda_foo)
#### Post date: [June 22, 2023, 6:09am UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/5 "2023-06-22T06:09:10Z")

</div>

Cool good to know. It wasn’t clear that project was active or being used by anyone.  
I’ve had a PR open against it since Feb to fix the mutable string/bytes warnings. [Add builds for 4.13, 4.14 and 5.0 by tmcgilchrist · Pull Request #7 · vbmithr/ocaml-thrift-lib · GitHub](https://github.com/vbmithr/ocaml-thrift-lib/pull/7)

---

<div class="post-metadata">

### Author: ![jonsterling](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/jonsterling/32/4430_2.png) [@jonsterling](https://discuss.ocaml.org/u/jonsterling)
#### Post date: [August 2, 2023, 11:32am UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/6 "2023-08-02T11:32:11Z")

</div>

I am also interested in this question. I have a lot of code that uses `formatter`, and I am doing something similar to @yawaramin in wrapping a flow in a formatter, but there is a mismatch between the models.

```auto
let formatter_of_flow flow =
  let out buf off len = 
    let cstruct = Cstruct.of_string ~off ~len buf in
    Eio.Flow.write flow [cstruct]
  in 
  let flush () = () in
  Format.make_formatter out flush 

```

I also noticed that writing to one of these formatters constructed from a `flow` is significantly slower than writing to a formatter constructed from an `out_channel`. Are there any suggestions, or should I hold off on using Eio for this?

---

<div class="post-metadata">

### Author: ![talex5](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/talex5/32/353_2.png) [@talex5](https://discuss.ocaml.org/u/talex5)
#### Post date: [August 2, 2023, 12:41pm UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/7 "2023-08-02T12:41:22Z")

</div>

`out_channel` is buffered, whereas a flow typically isn’t. You could try something like this:

```ocaml
let formatter_of_writer w =
  let out buf off len =
    Eio.Buf_write.string w buf ~off ~len
  in 
  let flush () = () in
  Format.make_formatter out flush

```

```ocaml
Eio.Buf_write.with_flow flow @@ fun b ->
let f = formatter_of_writer b in
...

```

---

<div class="post-metadata">

### Author: ![jonsterling](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/jonsterling/32/4430_2.png) [@jonsterling](https://discuss.ocaml.org/u/jonsterling)
#### Post date: [August 2, 2023, 1:06pm UTC](https://discuss.ocaml.org/t/thrift-eio-flow-transport-suggestions/12385/8 "2023-08-02T13:06:01Z")

</div>

@talex5 Thanks for this suggestion, that is a bit better performing.
