Skip to content

Commit

Permalink
Optimizations for request execution happy path (#3143)
Browse files Browse the repository at this point in the history
Optimize for request happy path
  • Loading branch information
kyri-petrou authored Sep 18, 2024
1 parent a52f6f6 commit 46980fd
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ private[zio] final case class ServerInboundHandler(

implicit private val unsafe: Unsafe = Unsafe.unsafe

private var routes: Routes[Any, Response] = _
private var runtime: NettyRuntime = _
private var handler: Handler[Any, Nothing, Request, Response] = _
private var runtime: NettyRuntime = _

val inFlightRequests: LongAdder = new LongAdder()
private val readClientCert = config.sslConfig.exists(_.includeClientCert)
Expand All @@ -58,7 +58,7 @@ private[zio] final case class ServerInboundHandler(
def refreshApp(): Unit = {
val pair = appRef.get()

this.routes = pair._1
this.handler = pair._1.toHandler
this.runtime = new NettyRuntime(pair._2)
}

Expand Down Expand Up @@ -88,7 +88,7 @@ private[zio] final case class ServerInboundHandler(
releaseRequest()
} else {
val req = makeZioRequest(ctx, jReq)
val exit = routes(req)
val exit = handler(req)
if (attemptImmediateWrite(ctx, req.method, exit)) {
releaseRequest()
} else {
Expand Down
15 changes: 7 additions & 8 deletions zio-http/shared/src/main/scala/zio/http/RoutePattern.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,15 @@ object RoutePattern {
tree.add(p, v)
}

private val wildcardsTree = roots.getOrElse(Method.ANY, null)

def get(method: Method, path: Path): Chunk[A] = {
val wildcards = roots.get(Method.ANY) match {
case None => Chunk.empty
case Some(value) => value.get(path)
val forMethod = roots.getOrElse(method, null) match {
case null => Chunk.empty
case value => value.get(path)
}

(roots.get(method) match {
case None => Chunk.empty
case Some(value) => value.get(path)
}) ++ wildcards
if (wildcardsTree eq null) forMethod
else forMethod ++ wildcardsTree.get(path)
}

def map[B](f: A => B): Tree[B] =
Expand Down
25 changes: 15 additions & 10 deletions zio-http/shared/src/main/scala/zio/http/Routes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,20 +245,25 @@ final case class Routes[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) { s
*/
def toHandler(implicit ev: Err <:< Response): Handler[Env, Nothing, Request, Response] = {
implicit val trace: Trace = Trace.empty
val tree = self.tree
Handler
.fromFunctionHandler[Request] { req =>
val chunk = tree.get(req.method, req.path)

if (chunk.length == 0) Handler.notFound
else if (chunk.length == 1) chunk(0)
else {
// TODO: Support precomputed fallback among all chunk elements:
chunk.tail.foldLeft(chunk.head) { (acc, h) =>
acc.catchAll { response =>
if (response.status == Status.NotFound) h
else Handler.fail(response)
chunk.length match {
case 0 => Handler.notFound
case 1 => chunk(0)
case n => // TODO: Support precomputed fallback among all chunk elements
var acc = chunk(0)
var i = 1
while (i < n) {
val h = chunk(i)
acc = acc.catchAll { response =>
if (response.status == Status.NotFound) h
else Handler.fail(response)
}
i += 1
}
}
acc
}
}
.merge
Expand Down
14 changes: 7 additions & 7 deletions zio-http/shared/src/main/scala/zio/http/codec/PathCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ object PathCodec {
def get(path: Path): Chunk[A] =
get(path, 0)

private def get(path: Path, from: Int, skipLiteralsFor: Set[Int] = Set.empty): Chunk[A] = {
private def get(path: Path, from: Int, skipLiteralsFor: Set[Int] = null): Chunk[A] = {
val segments = path.segments
val nSegments = segments.length
var subtree = self
Expand All @@ -801,7 +801,7 @@ object PathCodec {
val segment = segments(i)

// Fast path, jump down the tree:
if (!skipLiteralsFor.contains(i) && subtree.literals.contains(segment)) {
if ((skipLiteralsFor.eq(null) || !skipLiteralsFor.contains(i)) && subtree.literals.contains(segment)) {

// this subtree segment have conflict with others
// will try others if result was empty
Expand Down Expand Up @@ -875,19 +875,19 @@ object PathCodec {

// Might be some other matches because trailing matches everything:
if (subtree ne null) {
subtree.others.get(SegmentCodec.trailing) match {
case Some(subtree) =>
result = result ++ subtree.value
case None =>
subtree.others.getOrElse(SegmentCodec.Trailing, null) match {
case null => ()
case subtree => result = result ++ subtree.value
}
}

if (trySkipLiteralIdx.nonEmpty && result.isEmpty) {
trySkipLiteralIdx = trySkipLiteralIdx.reverse
val skipLiteralsFor0 = if (skipLiteralsFor eq null) Set.empty[Int] else skipLiteralsFor
while (trySkipLiteralIdx.nonEmpty && result.isEmpty) {
val skipIdx = trySkipLiteralIdx.head
trySkipLiteralIdx = trySkipLiteralIdx.tail
result = get(path, from, skipLiteralsFor + skipIdx)
result = get(path, from, skipLiteralsFor0 + skipIdx)
}
result
} else result
Expand Down

0 comments on commit 46980fd

Please sign in to comment.