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

Kyo grpc #723

Draft
wants to merge 52 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
981f886
Use Typelevel scalac-options
steinybot Jun 2, 2024
7c9dd57
Fix warnings
steinybot Jun 2, 2024
d1e10b6
Fix unused warning for Scala 2.12
steinybot Jun 2, 2024
914ab9c
Add kyo-grpc project
steinybot May 28, 2024
0d3c3fe
Add temporary gRPC server example
steinybot May 30, 2024
1c1de9f
Create new projects from scalapb template
steinybot Jun 2, 2024
a3ecc0d
Fix build
steinybot Jun 2, 2024
8d51bb4
Attempt to fix collection conversions
steinybot Jun 2, 2024
1f85c2b
Fix collection conversions
steinybot Jun 2, 2024
db630b5
Use Scala 2.12 for code gen classpath
steinybot Jun 2, 2024
68ba2af
Get JS E2E tests working
steinybot Jun 3, 2024
923de9d
Migrate test to scalatest
steinybot Jun 3, 2024
64ad69d
Add GrpcResponse
steinybot Jun 3, 2024
785c80c
Update gRPC example to use GrpcResponses
steinybot Jun 3, 2024
7b42cc7
Fix build aggregates
steinybot Jun 3, 2024
cec0856
Add PB.targets
steinybot Jun 3, 2024
13b065c
Update example
steinybot Jun 3, 2024
6bc4ffc
Generate single file unary method
steinybot Jun 4, 2024
f05fa0a
Add AbstractService parent
steinybot Jun 4, 2024
f47c11d
Partial commit for code gen
steinybot Jun 9, 2024
8b66f96
Fix object builder
steinybot Jun 16, 2024
a643dbb
Generate service companion
steinybot Jun 18, 2024
7beb4b6
Simplify generated code
steinybot Jun 18, 2024
6ac8580
Handle shutdown gracefully
steinybot Jun 21, 2024
2ce84e4
Update test.proto
steinybot Jun 23, 2024
e39ec54
Fix implicits
steinybot Jun 23, 2024
0d3ed0c
Add server test
steinybot Jun 23, 2024
f74c6ee
Add test for aborts
steinybot Jun 23, 2024
6bdad01
Add fail test
steinybot Jun 23, 2024
845835c
Tidy up tests
steinybot Jun 23, 2024
74469bc
Start writing client
steinybot Jul 13, 2024
c5804cc
Add basic client implementation
steinybot Jul 15, 2024
e4905b7
Update grpc example
steinybot Jul 21, 2024
0ef2cae
Add placeholder grpc benchmarks
steinybot Aug 4, 2024
9601353
Implement basic unary benchmark
steinybot Aug 5, 2024
952a84e
Add grpc server unary bench
steinybot Aug 6, 2024
9328546
Add gRPC helpers
steinybot Sep 3, 2024
269a903
Merge remote-tracking branch 'upstream/main' into kyo-grpc
steinybot Sep 16, 2024
76a3893
Merge remote-tracking branch 'upstream/main' into kyo-grpc
steinybot Sep 16, 2024
1ad73e2
Handle abort first
steinybot Sep 16, 2024
dada1e3
Update gRPC example
steinybot Sep 16, 2024
3be41ea
Rename package
steinybot Sep 16, 2024
0448c7b
Fix gRPC tests
steinybot Sep 17, 2024
f2cb617
Add gRPC stream observers
steinybot Sep 23, 2024
48827a1
Implement streaming handlers
steinybot Sep 26, 2024
71cc5d8
Merge remote-tracking branch 'upstream/main' into kyo-grpc
steinybot Sep 26, 2024
3c2675d
Merge remote-tracking branch 'upstream/main' into kyo-grpc
steinybot Oct 1, 2024
acbaa4a
Continue implementing gRPC streams
steinybot Oct 1, 2024
6094c66
Fix tests
steinybot Oct 1, 2024
b171c43
Implement client streaming
steinybot Oct 3, 2024
715b465
Get streaming working
steinybot Oct 5, 2024
33338c2
Fix test service
steinybot Oct 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ fileOverride {
"glob:**/kyo-stats-otel/**" {
runner.dialect = scala212source3
}
"glob:**/kyo-grpc/code-gen/**" {
runner.dialect = scala212source3
}
"glob:**/scala-3/**" {
runner.dialect = scala3
}
Expand Down
166 changes: 158 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ lazy val kyoJVM = project
.in(file("."))
.settings(
name := "kyoJVM",
`kyo-settings`
`kyo-settings`,
crossScalaVersions := Seq.empty
)
.aggregate(
`kyo-scheduler`.jvm,
Expand All @@ -96,14 +97,16 @@ lazy val kyoJVM = project
`kyo-zio`.jvm,
`kyo-cats`.jvm,
`kyo-combinators`.jvm,
`kyo-grpc`.jvm,
`kyo-examples`.jvm
)

lazy val kyoJS = project
.in(file("js"))
.settings(
name := "kyoJS",
`kyo-settings`
`kyo-settings`,
crossScalaVersions := Seq.empty
)
.aggregate(
`kyo-scheduler`.js,
Expand All @@ -116,7 +119,8 @@ lazy val kyoJS = project
`kyo-test`.js,
`kyo-zio`.js,
`kyo-cats`.js,
`kyo-combinators`.js
`kyo-combinators`.js,
`kyo-grpc`.js
)

lazy val kyoNative = project
Expand All @@ -139,6 +143,7 @@ lazy val `kyo-scheduler` =
.settings(
`kyo-settings`,
scalacOptions ++= scalacOptionToken(ScalacOptions.source3).value,
Test / scalacOptions --= scalacOptionToken(ScalacOptions.languageStrictEquality).value,
crossScalaVersions := List(scala3Version, scala212Version, scala213Version),
libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % Test,
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.8" % Test
Expand Down Expand Up @@ -232,6 +237,7 @@ lazy val `kyo-stats-registry` =
.settings(
`kyo-settings`,
scalacOptions ++= scalacOptionToken(ScalacOptions.source3).value,
scalacOptions --= scalacOptionToken(ScalacOptions.languageStrictEquality).value,
libraryDependencies += "org.hdrhistogram" % "HdrHistogram" % "2.2.2",
libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % Test,
crossScalaVersions := List(scala3Version, scala212Version, scala213Version)
Expand Down Expand Up @@ -344,6 +350,128 @@ lazy val `kyo-cats` =
libraryDependencies += "org.typelevel" %%% "cats-effect" % catsVersion
).jsSettings(
`js-settings`
).jvmSettings()

lazy val `kyo-grpc` =
crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.settings(
crossScalaVersions := Seq.empty,
publishArtifact := false,
publish := {},
publishLocal := {}
)
.aggregate(
`kyo-grpc-core`,
`kyo-grpc-code-gen`,
`kyo-grpc-e2e`
)

lazy val `kyo-grpc-jvm` =
`kyo-grpc`
.jvm
.aggregate(`protoc-gen-kyo-grpc`.componentProjects.map(p => p: ProjectReference) *)

// TODO: Split this into client and server
lazy val `kyo-grpc-core` =
crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Full)
.in(file("kyo-grpc") / "core")
.dependsOn(`kyo-core`)
.settings(`kyo-settings`)
.jvmSettings(
libraryDependencies ++= Seq(
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion,
"io.grpc" % "grpc-api" % "1.64.0",
// It is a little unusual to include this here but it greatly reduces the amount of generated code.
"io.grpc" % "grpc-stub" % "1.64.0"
)
).jsSettings(
`js-settings`,
libraryDependencies ++= Seq( //
"com.thesamet.scalapb.grpcweb" %%% "scalapb-grpcweb" % "0.7.0")
)

// TODO: Split this into shared, client, and server
// TODO: Do we need code gen for JS?
lazy val `kyo-grpc-code-gen` =
crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Full)
.in(file("kyo-grpc") / "code-gen")
.enablePlugins(BuildInfoPlugin)
.settings(
`kyo-settings`,
buildInfoKeys := Seq[BuildInfoKey](name, organization, version, scalaVersion, sbtVersion),
// TODO: What package to use here?
buildInfoPackage := "kyo.grpc.compiler",
// TODO: Which versions should this be for?
crossScalaVersions := List(scala212Version, scala213Version, scala3Version),
scalacOptions ++= scalacOptionToken(ScalacOptions.source3).value,
libraryDependencies ++= Seq(
"com.thesamet.scalapb" %% "compilerplugin" % scalapb.compiler.Version.scalapbVersion,
"org.scala-lang.modules" %%% "scala-collection-compat" % "2.12.0",
"org.typelevel" %%% "paiges-core" % "0.4.3"
)
).jsSettings(
`js-settings`
)

lazy val `kyo-grpc-code-gen_2.12` =
`kyo-grpc-code-gen`
.jvm
.settings(scalaVersion := scala212Version)

lazy val `kyo-grpc-code-genJS_2.12` =
`kyo-grpc-code-gen`
.js
.settings(scalaVersion := scala212Version)

// TODO: Why this name?
// TODO: Can these meta projects be in the sub directory?
lazy val `protoc-gen-kyo-grpc` =
protocGenProject("protoc-gen-kyo-grpc", `kyo-grpc-code-gen_2.12`)
.settings(
`kyo-settings`,
scalaVersion := scala212Version,
crossScalaVersions := Seq(scala212Version),
// TODO: Does it not auto-discover it?
Compile / mainClass := Some("kyo.grpc.compiler.CodeGenerator")
)
.aggregateProjectSettings(
scalaVersion := scala212Version,
crossScalaVersions := Seq(scala212Version)
)

lazy val `kyo-grpc-e2e` =
crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Full)
.in(file("kyo-grpc") / "e2e")
.enablePlugins(LocalCodeGenPlugin)
.dependsOn(`kyo-grpc-core`)
.settings(
`kyo-settings`,
publish / skip := true,
Compile / PB.protoSources += sharedSourceDir("main").value / "protobuf",
Compile / PB.targets := Seq(
scalapb.gen() -> (Compile / sourceManaged).value / "scalapb",
// TODO: Make this nicer. Like scalapb.zio_grpc.ZioCodeGenerator.
genModule("kyo.grpc.compiler.CodeGenerator$") -> (Compile / sourceManaged).value / "scalapb"
),
Compile / scalacOptions ++= scalacOptionToken(ScalacOptions.warnOption("conf:src=.*/src_managed/main/scalapb/kgrpc/.*:silent")).value
).jvmSettings(
codeGenClasspath := (`kyo-grpc-code-gen_2.12` / Compile / fullClasspath).value,
libraryDependencies ++= Seq(
"io.grpc" % "grpc-netty" % "1.65.1"
)
).jsSettings(
`js-settings`,
codeGenClasspath := (`kyo-grpc-code-genJS_2.12` / Compile / fullClasspath).value,
libraryDependencies ++= Seq(
"com.thesamet.scalapb.grpcweb" %%% "scalapb-grpcweb" % "0.7.0"
)
)

lazy val `kyo-combinators` =
Expand Down Expand Up @@ -383,12 +511,27 @@ lazy val `kyo-bench` =
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("kyo-bench"))
.enablePlugins(JmhPlugin)
.dependsOn(`kyo-core`)
.dependsOn(`kyo-sttp`)
.dependsOn(`kyo-scheduler-zio`)
.enablePlugins(JmhPlugin, LocalCodeGenPlugin)
.dependsOn(
`kyo-core`,
`kyo-grpc-core`,
`kyo-sttp`,
`kyo-scheduler-zio`
)
.settings(
`kyo-settings`,
publish / skip := true,
Compile / PB.protoSources += baseDirectory.value.getParentFile / "src" / "main" / "protobuf",
Compile / PB.targets := {
val scalapbDir = (Compile / sourceManaged).value / "scalapb"
Seq(
scalapb.gen() -> scalapbDir,
scalapb.zio_grpc.ZioCodeGenerator -> scalapbDir,
genModule("kyo.grpc.compiler.CodeGenerator$") -> scalapbDir
)
},
codeGenClasspath := (`kyo-grpc-code-gen_2.12` / Compile / fullClasspath).value,
Compile / scalacOptions ++= scalacOptionToken(ScalacOptions.warnOption("conf:src=.*/src_managed/main/scalapb/kgrpc/.*:silent")).value,
Test / testForkedParallel := true,
// Forks each test suite individually
Test / testGrouping := {
Expand Down Expand Up @@ -423,13 +566,15 @@ lazy val `kyo-bench` =
libraryDependencies += "dev.zio" %% "zio-concurrent" % zioVersion,
libraryDependencies += "dev.zio" %% "zio-prelude" % "1.0.0-RC31",
libraryDependencies += "com.softwaremill.ox" %% "core" % "0.0.25",
libraryDependencies += "com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion,
libraryDependencies += "co.fs2" %% "fs2-core" % "3.11.0",
libraryDependencies += "org.http4s" %% "http4s-ember-client" % "0.23.28",
libraryDependencies += "org.http4s" %% "http4s-dsl" % "0.23.28",
libraryDependencies += "dev.zio" %% "zio-http" % "3.0.1",
libraryDependencies += "io.grpc" % "grpc-netty" % "1.65.1",
libraryDependencies += "io.vertx" % "vertx-core" % "4.5.10",
libraryDependencies += "io.vertx" % "vertx-web" % "4.5.10",
libraryDependencies += "org.scalatest" %% "scalatest" % scalaTestVersion % Test
libraryDependencies += "org.scalatest" %% "scalatest" % scalaTestVersion % Test,
)

lazy val rewriteReadmeFile = taskKey[Unit]("Rewrite README file")
Expand All @@ -442,6 +587,7 @@ lazy val readme =
.crossType(CrossType.Full)
.in(file("target/readme"))
.enablePlugins(MdocPlugin)
.disablePlugins(ProtocPlugin)
.settings(
`kyo-settings`,
mdocIn := new File("./../../README-in.md"),
Expand Down Expand Up @@ -489,3 +635,7 @@ def scalacOptionTokens(proposedScalacOptions: Set[ScalacOption]) = Def.setting {
val version = ScalaVersion.fromString(scalaVersion.value).right.get
ScalacOptions.tokensForVersion(version, proposedScalacOptions)
}

def sharedSourceDir(conf: String) = Def.setting {
CrossType.Full.sharedSrcDir(baseDirectory.value, conf).get.getParentFile
}
54 changes: 54 additions & 0 deletions kyo-bench/src/main/protobuf/testservice.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
syntax = "proto3";

option java_multiple_files = true;
// Don't use kyo here because otherwise it cannot derive the Frame.
option java_package = "kgrpc.helloworld";
option java_outer_classname = "HelloWorldProto";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}

// Sends a litany of greetings
//rpc SayHelloStreaming (HelloRequest) returns (stream HelloReply) {}
}

// The actual message exchanged by the client and the server.
message Hello {
string name = 1;
double d = 2;
float f = 3;
bool b = 4;
int32 n = 5;
int64 l = 6;
oneof choice {
string c1 = 7;
bool c2 = 8;
}
message Pet {
enum Color {
BLACK = 0;
WHITE = 1;
BLUE = 2;
RED = 3;
YELLOW = 4;
GREEN = 5;
}
string name = 1;
Color color = 2;
}
repeated Pet pets = 9;
}

// The request message from the client.
message HelloRequest {
Hello request = 1;
}

// The response message from the server.
message HelloReply {
Hello response = 1;
}
34 changes: 34 additions & 0 deletions kyo-bench/src/main/scala/kyo/bench/GrpcE2EUnaryBench.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kyo.bench

import io.grpc.Grpc
import kgrpc.helloworld.testservice.*
import kyo.*
import kyo.bench.GrpcService.*
import kyo.grpc.GrpcRequest
import scalapb.zio_grpc.Server
import zio.UIO

class GrpcE2EUnaryBench extends Bench.ForkOnly(reply):

override def catsBench() =
???

override def kyoBenchFiber() =
Resource.run(
GrpcRequest.run(
for
_ <- createServer(port)
client <- createClient(port)
yield client.sayHello(request)
).map(_.getOrThrow)
)

override val zioRuntimeLayer =
super.zioRuntimeLayer.merge(serverLayer).merge(clientLayer)

override def zioBench() =
ZioTestservice.GreeterClient.sayHello(request)
.orDie
.asInstanceOf[UIO[HelloReply]]

end GrpcE2EUnaryBench
41 changes: 41 additions & 0 deletions kyo-bench/src/main/scala/kyo/bench/GrpcServerUnaryBench.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package kyo.bench

import io.grpc.ManagedChannelBuilder
import java.util.concurrent.TimeoutException
import java.util.concurrent.TimeUnit
import kgrpc.helloworld.testservice.*
import kyo.*
import kyo.bench.GrpcService.*
import org.openjdk.jmh.annotations.TearDown
import scalapb.zio_grpc.Server
import zio.ZIO

class GrpcServerUnaryBench extends Bench.ForkOnly(reply):

private val channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build
private val blockingStub = GreeterGrpc.blockingStub(channel)

@TearDown
def shutdownChannel() =
val shutdown = channel.shutdownNow().awaitTermination(10, TimeUnit.SECONDS)
if !shutdown then throw TimeoutException("Channel did not shutdown within 10 seconds.")
end shutdownChannel

override def catsBench() =
???

override def kyoBenchFiber() =
Resource.run {
for
_ <- createServer(port)
reply <- IO(blockingStub.sayHello(request))
yield reply
}

override val zioRuntimeLayer =
super.zioRuntimeLayer.merge(serverLayer)

override def zioBench() =
ZIO.attempt(blockingStub.sayHello(request)).orDie

end GrpcServerUnaryBench
Loading
Loading