Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prune the environment when installing an app #2833

Merged
merged 3 commits into from
May 11, 2024

Conversation

kyri-petrou
Copy link
Collaborator

@kyri-petrou kyri-petrou commented May 7, 2024

Hi team 👋

Context

There is a bug that's been around for some time (probably since the 0.x days) that I never paid too much attention to before because I didn't understand much about ZIO.

Currently, when we bootstrap / wire an app, we provide the current ZEnvironment in the NettyRuntime. This environment, however, contains pretty much all the services that were used to wire the app. Let's take the following app as an example:

object HelloWorld extends ZIOAppDefault {
  final class Service {
    def printEnv: UIO[Unit] = ZIO.environment.debug.unit
  }

  val homeRoute = Method.GET / Root -> handler(ZIO.serviceWithZIO[Service](_.printEnv.as(Response.text("foo"))))

  override val run =
    Server.serve(Routes(homeRoute)).provide(Server.default, ZLayer.succeed(new Service))
}

In this case, sending a request to the server will print the following in the console. As you can see, the environment contains 6 services in this case, where in theory it should only be containing our Service.

ZEnvironment(EventLoopGroup -> io.netty.channel.nio.NioEventLoopGroup@81f8e, ChannelFactory[=ServerChannel] -> zio.http.netty.ChannelFactories$$anon$1@41f33ad, Server::Config -> Config(None,0.0.0.0/0.0.0.0:8080,false,true,No,None,Disabled(102400),4096,8192,true,PT10S,WebSocketConfig(None,10000,-1,true,NormalClosure,true,SocketDecoder(65536,true,false,false,true,true)),None), NettyConfig -> NettyConfig(SIMPLE,AUTO,0,PT2S,PT15S), HelloWorld::Service -> example.HelloWorld$Service@55615130, Server -> ServerLive(NettyDriver(Config(None,0.0.0.0/0.0.0.0:8080,false,true,No,None,Disabled(102400),4096,8192,true,PT10S,WebSocketConfig(None,10000,-1,true,NormalClosure,true,SocketDecoder(65536,true,false,false,true,true)),None)),8080))

Why is this a problem?

The way that ZEnvironment works, is that lookups are O(n) complex (n = size of environment) when a service is accessed for the first time it goes into a private cache and then subsequent access for that service is O(1) complex. Since the environment is immutable, anything that adds something to it, e.g., ZIO.scoped creates a new instance to it, so accessing that service becomes O(n) again. So having an environment as lean as possible will yield better performance

Main change

The changes in this PR ensure that the environment is pruned to contained only the services that are required to run the app. The downside to this is that we require an implicit EnvironmentTag now in the install and serve methods. I think that in most cases this should be okay, since most users would be using install or serve when R is statically known.

Alternative approach (no breaking changes)

An alternative that doesn't involve breaking changes would be to revert the implicit EnvironmentTag changes, and keep only the change made to AppRef. This way, we leave it up to the user to properly clean the environment prior to calling serve or install. The downside to this is that it's unlikely that users will do this

@987Nabil 987Nabil merged commit 2a565a3 into zio:main May 11, 2024
46 checks passed
@kyri-petrou kyri-petrou deleted the prune-environment-after-wiring branch May 11, 2024 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants