Skip to content
This repository was archived by the owner on Jul 1, 2021. It is now read-only.

Make RPCModules independent of concrete chain #121

Merged

Conversation

cburgdorf
Copy link
Contributor

@cburgdorf cburgdorf commented Jan 8, 2019

What was wrong?

RPC Modules were locked to depend on BaseAsyncChain .

How was it fixed?

  1. RPCModule is genreic over TChain
  2. RPCServer dropped dependency on the chain so that all it cares about is the name which means it is satisfied by the new non-generic BaseRPCModule.
  3. The chain reset mechanism is now event bus based (making it possible to drop the chain dependency on the RPCServer

Cute Animal Picture

put a cute animal picture link inside the parentheses

RPCModules need to access the chain regulary
so it makes sense for them to depend and provide
access to the chain. However, the RPCServer
doesn't care about the concrete chain type
and we can get away with making it generic
since Eth1 and Beacon chain don't share a
common ancestor.
`chain` and `event_bus` are being used in
child classes and should hence not be written
with _ (underscore) notation.
@cburgdorf cburgdorf changed the title Christoph/feat/rpc beacon modules Make RPCModules independent of concrete chain Jan 8, 2019
Copy link
Member

@pipermerriam pipermerriam left a comment

Choose a reason for hiding this comment

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

In order to accomplish #79 I believe that we end up needing to completely or almost completely stop using a remote Chain object.

Ignoring the special evm_resetToGenesisFixture method, that implies that all of our RPC modules which need access to the Chain need to be converted to use one of:

  • A: Event bus based data retrieval
  • B: A fully local Chain instance using a local database connection (probably a read-only one)

Doing B depends on database architecture that we cannot support with our existing LevelDB backend.

This leaves us with option A which requires us to create a new service that exposes Chain data over the event bus.

I recognize this pull request isn't exactly targeting #79 but I'm curious if it can be reworked to bring us closer to that goal. Thoughts?

@cburgdorf
Copy link
Contributor Author

The JSON-RPC Server already runs in it's own isolated process and the Chain is a local chain with only the DB ending up being proxied through another process as that's a current requirement that comes from using LevelDB.

As I said in #79, I don't think we should funnel all db read/writes through the event bus and instead focus on moving to something like RocksDB to get rid of remaining process hoping (caused by the db proxy to leveldb).

I believe funneling all db access through the eventbus will almost certainly have a worse performance than going through the db proxy, because:

  • The native Python proxy code is much more likely to be already heavily optimized for performance whereas the EventBus is still super young alpha software

  • The EventBus is optimized for convenience and e.g. uses multiple process hops internally whereas the db proxy has a more direct connection.

@cburgdorf
Copy link
Contributor Author

Oh on

This leaves us with option A which requires us to create a new service that exposes Chain data over the event bus.

I want to highlight that just exposing the chain data through another service doesn't buy us anything here because that other service would still need to talk to the DbProxy unless you want to create one dedicated service for all db access as a replacement for the DbProxy which I had assumed with my previous comment.

So, if we just add another service to expose Chain access you end up with: JSON-RPC process -> Chain process -> DbProxy whereas in this PR it is just JSON-RPC process -> DbProxy (since the chain is already a local one).

@pipermerriam
Copy link
Member

👍 on the correction that this is less about the Chain and more about the core database access which basically means my whole comment can be disregarded. There's one section I want to comment on but I'll do it separately.

def initialize_modules(modules: Iterable[Type[RPCModule]],
chain: BaseAsyncChain,
event_bus: Endpoint) -> Iterable[RPCModule]:
def initialize_modules(modules: Iterable[Type[RPCModule[TChain]]],
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious about us re-working this API to remove the chain component and instead have the signature be something like initialize_modules(module_classes, trinity_config, event_bus). This should allow any module that needs Chain access to connect to the chain itself, which I think also means that we could drop the Generic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem with this is that under testing, we want to inject a TestChain (which obviously we can't resolve through the TrinityConfig). And if we think about it, being able to pass chain instances into RPCModules gives us much more flexibility in general. The chain is what most of the modules want to access anyway.

Copy link
Member

Choose a reason for hiding this comment

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

The chain is what most of the modules want to access anyway.

There are plenty of modules that don't need the chain which is why I want to get the Chain requirement fully removed from the RPCModule concept.

As for testing and needing to inject a TestChain there are plenty of ways we can solve that problem.

RPC APIs that don't need the Chain

Copy link
Contributor Author

@cburgdorf cburgdorf Jan 9, 2019

Choose a reason for hiding this comment

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

There are plenty of modules that don't need the chain which is why I want to get the Chain requirement fully removed from the RPCModule concept.

Ah ok. Well, I kinda removed the Chain already by creating an BaseRPCModule that is basically just an interface that defines an abstract name property.

class BaseRPCModule(ABC):

    @property
    @abstractmethod
    def name(self) -> str:
        pass

That's the only thing RPC modules need to implement to work with the RPCServer because BaseRPCModule is the only thing the server cares about.

But I realize that I missed the opportunity to make that more clear by providing one class RPCModule that uses a chain and using that class even for modules that don't need the chain (e.g. net).

I'm going to iterate on that and will further comment after I refactored some things to be clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@pipermerriam I pushed another commit for some cleanups and added inline comments to explain.

yield Eth(chain, event_bus)
yield EVM(chain, event_bus)
yield Net(event_bus)
yield Web3()
Copy link
Contributor Author

@cburgdorf cburgdorf Jan 9, 2019

Choose a reason for hiding this comment

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


The BaseRPCModule has zero implementation and only defines an abstract name (so more of an interface than a base class). We initialize every module with the dependencies that the specific module needs. But since no one outside of the module cares about these internals the returned value can be treated as Tuple[BaseRPCModule, ...]

@@ -40,29 +35,20 @@ def name(self) -> str:
pass


class RPCModule(BaseRPCModule, Generic[TChain]):
class ChainBasedRPCModule(BaseRPCModule, Generic[TChain]):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just to have a DRY base class for all the modules that do depend on a chain. No part of the RPC system cares about it except the module itself. Every other code place just deals with BaseRPCModule.


class Net(Eth1RPCModule):
def __init__(self, event_bus: Endpoint):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Net module even defines its very own __init__ as it needs an event bus but no chain.

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

Successfully merging this pull request may close these issues.

2 participants