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

Provide a Scope per server call (#3197) #3276

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

987Nabil
Copy link
Contributor

fixes #3197
/claim #3197

@987Nabil 987Nabil force-pushed the sope-per-request branch 7 times, most recently from 6d31d72 to a5d85d1 Compare January 19, 2025 12:19
@@ -98,7 +98,7 @@ final case class TestServer(driver: Driver, bindPort: Int) extends Server {
_ <- driver.addApp(provided, r)
} yield ()

override def install[R](routes: Routes[R, Response])(implicit
override def install[R](routes: Routes[Scope & R, Response])(implicit
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of this Scope requirement?

I'd prefer hiding it from the Routes because it makes users to provide Scope that is not actually used.

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 Scope has to be here, so that it is "eaten" up by the method. The effect that is returned has no more requirement of scope. That means, it is not possible to use a Scope that extends over a single request from inside a request. That is intentionally

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. I misinterpreted the code due to this line.

I also wondered if we could do the elimination earlier, RoutePattern#->, Routes.apply or etc. for example, but it could be error prone and let the compiler do more work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, the routes is the right place. We need to close the Scope in the driver, after netty write is over.
If we eliminate the Scope earlier, we would need to close the scope earlier too.
Else you do route.run(..) and your scope is not closed/provided. That would be a bug

Copy link
Contributor

Choose a reason for hiding this comment

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

By 'eliminating', I mean something like this:

trait Route[-Env, +Err] {
  def apply(request: Request): ZIO[Scope & Env, Response, Response] = ???
}

case class RoutePattern[A](...) {
  def ->[Env, Err, I](handler: Handler[Scope & Env, Err, I, Response]): Route[Env, Err] = ???
}

Unlike the other environments, Scope is a local requirement per request. I think this encoding reveals the relationship more clearly, but there's two downsides that we need to modify every route forming function including Endpoint DSL and the burden to the compiler of the added type calculation per route . In this respect, I think the current design in this PR is still good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@guersam I tried that out. It complicates the code, has negative impact on type inference and all just to save Scope on the type of Routes seems like a bad trade off

Copy link
Contributor

Choose a reason for hiding this comment

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

@guersam I tried that out. It complicates the code, has negative impact on type inference and all just to save Scope on the type of Routes seems like a bad trade off

This was my conclusion on #3229 as well, I agree with @987Nabil this is the best approach to not make it very convoluted.

@987Nabil 987Nabil force-pushed the sope-per-request branch 2 times, most recently from 3c473b1 to 09f624a Compare January 20, 2025 17:26
@jgranstrom
Copy link
Contributor

FWIW I tested this on the real world case of #3197 and it looks good.

@987Nabil 987Nabil force-pushed the sope-per-request branch 2 times, most recently from d3c02e3 to 5ccb804 Compare February 3, 2025 08:39
@987Nabil
Copy link
Contributor Author

987Nabil commented Feb 3, 2025

@guersam @jgranstrom @kyri-petrou I changed the design after talking to @jdegoes
please take a look again.

@guersam
Copy link
Contributor

guersam commented Feb 3, 2025

I'm excited to see how the new design looks in idiomatic ZIO. It must have involved subtle internal design decisions and thorough changes across a wide range of the codebase. I really appreciate your continuous efforts to improve it!

@987Nabil 987Nabil force-pushed the sope-per-request branch 2 times, most recently from a1fb9bf to d96627b Compare February 3, 2025 10:07
@@ -1051,6 +1058,9 @@ object Handler extends HandlerPlatformSpecific with HandlerVersionSpecific {
def fromResponseZIO[R, Err](getResponse: ZIO[R, Err, Response]): Handler[R, Err, Any, Response] =
fromZIO(getResponse)

def scoped[R]: ScopedPartiallyApplied[R] =
new ScopedPartiallyApplied[R]()
Copy link
Member

Choose a reason for hiding this comment

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

Make:

_scopedPartiallyApplied.asInstanceOf[ScopePartiallyApplied[R]]

where:

private val _scopedPartiallyApplied = new ScopedPartiallyApplied[Any]()

@@ -181,7 +181,7 @@ object Middleware extends HandlerAspects {
handler { (req: Request) =>
if (req.headers.contains(header.name)) h(req)
else h(req.addHeader(make))
}
}.asInstanceOf[Handler[Env1, Response, Request, Response]]
Copy link
Member

Choose a reason for hiding this comment

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

Handler.scoped here and elsewhere

Handler
.fromZIO(handler(routePattern).map(_.sandbox))
.flatten
.asInstanceOf[Handler[Env, Response, Request, Response]]
Copy link
Member

Choose a reason for hiding this comment

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

Ditto

@jdegoes
Copy link
Member

jdegoes commented Feb 5, 2025

Ideally this would NOT allow R to extend Scope:

trait Server {

  /**
   * Installs the given HTTP application into the server.
   */
  def install[R](routes: Routes[R, Response])(implicit trace: Trace, tag: EnvironmentTag[R]): URIO[R, Unit]

Using a macro (making this old version private so it's there for backward compatibility only).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants