EPF C4 - Tomás Arjovsky - Week 9

These are the updates for the week of 2023/09/11.

First update

Tracing and profiling

We realized that we had a high-CPU consumption issue in our elixir app, which was unreasonable for the amount of work the app was doing. To make it easier to debug I added a series of tools to the project:

  • rexbug: a tracing library that allows the code to be instrumented and detect calls to functions specified with module, name and arity. It can also include the stack trace for those detected calls.
  • etop a tool similar to htop but for elixir processes.
  • I wrote a default-friendly wrapper over eep, a profiling library that instruments live production code and generates a profile of which processes or functions take the most CPU time.

The library has the following easy to use interface:

LambdaEthereumConsensus.Profile.build()

It generates traces that can be read with QCachegrind and look like the following screenshot:

This was a confirmation of the ports discussions last week: we can't have NIFs with blocking calls. We'll need to move over to ports. For now, we decided to implement a quick work-aroudn with goroutines in the go side.

Second Update

Built a proof of concept for LibP2P over ports, with a GenServer in elixir that owns the port, sends requests and receives notifications. This is the PR for it.

sequenceDiagram
    participant gs as GenServer
    participant cs as Go Command server
    participant tg as Topic consumer goroutine
    
    gs ->> cs: proto[SubscribeToTopicCommand]
    cs ->> tg: Create goroutine
    cs -->> gs: proto[OkResponse]
    tg ->> gs: proto[TopicNotification]

This PoC doesn't actually do any LibP2P work but serves as a basis for the big refactor to come. It shows how to:

  1. Create and manage a port, owned by a genserver.
  2. Send messages and notifications encoded with protobuf and size-delimited with 32-bit big endian size indicators.
  3. Have extendable protobuf oneofs for commands and responses, so we can add new types of them without needing to change the architecture, only add specific handlers for each new message.
  4. launch goroutines when subscribing and stop them when unsubscribing, using buffered go channels to communicate between goroutines.

The code can be used as-is, replacing the contents of some functions to the libp2p code that we already have for the current NIF implementation.

Select a repo