 All right, I'd like to thank everyone who is joining us today. Welcome to today's CNCF webinar, announcing open source GRPC Kotlin. I'm Kristy Tan, Marketing Communications Manager at CNCF. I'll be moderating today's webinar. We would like to welcome our presenter today, James Ward, Developer Advocate at Google Cloud Platform. A few housekeeping items before we get started. During the webinar, you are not able to talk as an attendee. There is a Q&A box at the bottom of your screen. Please feel free to drop your questions in there, and we'll get to as many as we can at the end. This is an official webinar of the CNCF, and as such is subject to the CNCF Code of Conduct. Please do not add anything to the chat or questions that would be in violation of that Code of Conduct. Basically, please be respectful of all of your fellow participants and presenters. Please also note that the recording and slides will be posted later today to the CNCF webinar page at cncf.io slash webinars. With that, I'll hand it over to James to kick off today's presentation. Great, thank you, Kristy. Thank you, everyone, for joining us today. So as Kristy said, I'm James Ward. I work at Google, and we're gonna be talking about an announcement that we just made yesterday about the open sourcing of GRPC Kotlin. So I wanna start out with just a little bit of an overview of Kotlin, for those of you that might not be familiar with it, and then do a little bit of an overview of GRPC, and then we'll get into the GRPC Kotlin announcement. And please ask questions as we go through. We have a number of people that are behind this open source project, also on today, and they will help answer questions during and then also at the end of the presentation. So ask away. All right, so let's start with a little bit of an overview of Kotlin and what it looks like if you haven't seen it before. Kotlin is a new-ish programming language, and it has a syntax that's kind of similar to Java. And so this makes it pretty easy to, if you're a Java developer or done other languages that have a similar syntax, it's pretty easy to get going with Kotlin. One big difference that you'll see right away between Kotlin and Java is that the type information is specified on the right-hand side, instead of the left-hand side. And the reason for this is that this enables the language compiler to do something called type inference. So in a lot of places, you can actually leave off the type information, and it's a lot easier to do that when it's on the right-hand side of the declaration versus the left-hand side. And so type inference does make the code usually look a lot cleaner and easier to parse and looks more expressive in a lot of cases. You do have to provide those types usually on function parameters, but they are optional on return types because they get inferred. Okay, so there's a couple of other interesting language features with Kotlin that I wanted to point out. One is that nullability is built into the type system. So you'll see that question mark after that configuration type. What's that saying is that that configuration may not be there. It's not necessarily null. It could be null if it's coming from Java and the Kotlin type system will handle that case for you, but this is saying we may not actually have a configuration parameter. And so then our code can account for that as needed. So that's one nice feature is having that nullability built into the type system. Another feature that's really great is first-class support for lambdas. So these are functions, inline functions that you can specify as essentially parameters to another function or method. And so it's really nice to have a concise syntax to define our functions. This comes in really handy in a lot of places. If you're doing Kotlin for UI programming, like in Android, there's a lot of callback things that benefit from the syntax. If you're on the server side, then things like being able to have operations that perform on a collection or a stream, it's really nice to have this lambdas syntax for having those functions, for doing transformations or filters, that kind of thing. One other nice feature that I use a lot in Kotlin is this template expression syntax where you can inject variables into strings so you don't have to do a string plus and then another string, that kind of thing. You can just embed the values right into the string template. So that's a nice feature. And then something that a lot of people love is that semicolons are optional. So in most cases, you can leave them out because they can just be inferred by the compiler. So some other things that are a little bit harder to point out in a simple code example, but we'll get into it a little bit later are co-routines. So co-routines are for doing asynchronous, concurrency, background tasks, that kind of thing, a really nice programming model around that. They're also in Kotlin is bidirectional interoperability with Java. So you can call Java objects and Java objects can call into Kotlin objects. And interesting thing about Kotlin related to this is that Kotlin didn't build its own collections library. Instead, they use the Java collections library, but to enhance them, they use another language feature in Kotlin called extension methods. And extension methods provide a really nice way to interoperate with Java APIs. The Kotlin syntax is expressive and concise, and we'll see that as we go through some examples in a little bit. And then it also the way there's some language features in Kotlin that enable it, make it really great for building DSLs and doing typescape builders. And we'll see some of that in a little bit. So why Kotlin? It's fairly new since 1.0 is only about four years ago, but it has grown a lot over time. It is now the standard language for building Android applications. And so it's getting a lot of usage from that, but it's now also growing in other areas as well. Like on the server side, there's great support in Java web frameworks like Spring Boot for Kotlin and a number of other web frameworks and RESTful frameworks that have great support for Kotlin as well. And just last fall, Kotlin became the number two most used JVM language behind Java, of course, and then Scala behind that. So it overtook Scala on that list. So a lot of great energy around Kotlin. Another example that I find pretty compelling and something that I really love in Kotlin is that in Java, we used to have to use Java beans syntax to write out our data classes, our ability to hold data in an object and you'd have to generate the getters and setters and equals and hash code. And yeah, your ID can generate those for you in your two string, but it sure is nice to have a concise syntax for defining these types of data classes that have values on them and the two string and equals and hash code methods are all generated by the compiler and that's just a data class in Kotlin. So that's something that I use a lot, makes my code a lot cleaner, easier to read. Okay, so we'll see a lot more Kotlin in a couple of minutes, but let's go on to GRPC. So GRPC combines together a couple of different pieces. The first piece is protocol buffers and protocol buffers are a nice declarative way to define messages and services. And then what we can do is take this declarative definition of our messages and services and we can then wrap services around those things, actual services that we run and interact with. So the protocol buffers, they are language agnostic. So we define them in one declarative format and then what we do is we generate from that declarative format the different, what are called stubs that we can interact with in Java or now in Kotlin or in JavaScript or whatever. So the nice thing is we have that language agnostic definition that allows us to communicate between different things without having to write our parsers and our data objects and all that in multiple languages. Protobufs also has great support for evolving protocols. So if we want to add fields to a message, we can do that and not break things. They're also strongly typed. So this gives us a strongly typed representation of our transport protocols. And so it's nice to have that over JSON, which obviously doesn't have any strong typing to it. So what we do with our protocol buffers is we generate our serializers and deserializers for our messages, those get generated by the protobuf protoc compiler, which you'll see in a little bit. And then we also generate our service stub. So you'll see that in this example, I've defined a greeter and it has an RPC service. So it's something that takes an input and returns an output of those messages. And what this will do is generate the client and the server to be able to use that service. So we generate both of those and then we get super fast network RPC. So the reason why we get super fast network RPC is one that the protocol is very concise and that's nice, but also GRPC is built on top of HTTP2. And HTTP2 brings with it a bunch of great features for doing this very optimized communication between a client and a server. So it uses a binary protocol, which allows us to be much more concise. It has stream support, so we don't need to use web sockets. We have native stream support in HTTP2. We can do stream multiplexing, so we can do multiple channels of communication over a single connection. And then it has other features like header compression to save bandwidth and that sort of thing. So that is our quick rundown on Kotlin and GRPC. And the announcement that we made yesterday was open sourcing GRPC Kotlin. And what that is is bringing together these two great technologies. So let's run through a little bit about what it is and then we'll see it for real. So what GRPC Kotlin does is it's gonna generate Kotlin friendly GRPC stubs for us. So the code that gets generated from our protobuf definition is going to work great with Kotlin. This is built on top of GRPC Java. So we're building on top of that because there is still a bunch of Java underneath the covers for a lot of this stuff. And so we build on top of that and add the Kotlin friendly pieces on top. But we can then add support for nice things in Kotlin like co-routines for doing async. And then also the Flow API, which is a stream-oriented API. So co-routines is a metaphor or as a way to do an async callback. So you're gonna go do something, let's say make a network call and then we're going to get a response back. But we don't want it to have to block everything in the normal blocking Java world. The program wouldn't be able to continue in that thread while we're waiting for that response to come back. But with co-routines, we can fire off the request and because we have a non-blocking network channel to be able to not block on, then we get that callback essentially when the response comes back. And so co-routines fit really well with that async call, RPC call. But the other paradigm that we can be in is using streams. And a stream allows us to broadcast events and then consume those broadcast events. And the Kotlin Flow API gives us a nice Kotlin way to interact with streams. And it's built on top of some foundational work, the reactive streams project, which supports the ability to do back pressure on these streams as well. So some nice features for stream handling. So to use Kotlin GRPC, GRPC Kotlin, we are going to plug into a Maven or Gradle build using the existing protobuf plugin. So we'll use the, if you were doing Java with GRPC, then it's gonna look very similar. And we'll see that in a little bit just with the Kotlin pieces built on top. And all this is available today in Maven Central as version 0.1.1. Okay, so let's take a look at a demo. And I'm gonna be showing demo and codes for the rest and feel free to ask your questions. And David or Lewis or Brent, if there's any questions that came in so far that need answering now, feel free to chime in. Okay, so let's start with our very simple, hello world example here. I've got my Gradle project and I have defined some versions for things. I am including the protobuf Gradle plugin and the Kotlin Gradle plugin. So that this project knows how to support Kotlin and knows how to generate protobufs. So then I'm going to apply those plugins. Now I do need to include the GRPC Kotlin stub and this is part of what we need to be able to, once the code is generated, there's some dependencies in that generated code and this is where those come from. Then we're gonna include our Kotlin standard stuff. We're gonna include our protobuf and GRPC standard stuff. So you'll see the protobuf Java, protobuf Java util. I'm using Netty, which GRPC is the actual server and client HTTP implementation. And then we get into our protobuf configuration where we are going to have a plugin to the protoc compiler, which is going to do the Java protoc compilation and then we're going to also do the Kotlin one. So we do both of those. And then there's a little bit more configuration in this but that's kind of it. Okay, so then I've got my protobuf definition. My protobuf definition, I have to find my service very similar to what you saw before. This is an RPC called say hello. It takes in an input of hello request and returns an output of hello reply. So then we can see our messages here. Hello request has a name and hello reply has a message. So that's our declarative protobuf for our GRPC service. Then let's go over to our server and our server is written in Kotlin. And there's some work here that is just starting up the server, listening on a port and doing that sort of thing and handling shutdown. The interesting piece of this is that I'm extending the Greeter GRPC-KT and the Greeter Co-Routine Imple Base. And so that was generated from my protobuf. And now I create an instance of this class and I override the say hello method and it's going to take the same parameters just that the interface had, which it came from that protobuf. It's going to take in a hello request and return a hello reply. And you'll see that this is a suspend fun, which means that it's using co-routines to do this. Okay, so what I'm going to do in this case, I'm going to handle my request and how I'm going to handle it is I'm going to create a hello reply. I'm going to use the builder for it. I'm going to set the message in that hello reply to say hello and then whatever the name that came in the request was. And then I'm going to build that and you'll see that build returns a hello reply. And so there I've implemented my simple server. So I'm going to start this server up so that we can see this thing actually work. So you'll see it's up and running, listening on that port. And then let's go take a look at our client. So for our client, let's first go down to our main method and have to do a little bit of setup here to connect to the server. So here's where I'm doing my message channel builder, giving it the server that I want to connect to. In my case, just local host in that port. And then I'm going to tell it, create my hello world client. And so this was part of the code that we'll see in a minute. And then we're going to figure out what we want to send. And if there was an argument to this program, then I'll use that. Otherwise I'll just use world. And now we call the hello world client dot greet method and give it this string user. So let's go take a look at that and see what's in there. So we've got our hello world client. It takes in a parameter of channel, which is closeable. And then we're using our stub that was generated from that protobuf definition. And now we've got this function greet, which takes in a name string. And then because we are using co-routines, we need to, in this case, we want to just run this thing, send it the message, get the response, and then exit. And so the way, in this case, instead of doing a suspend fund, is we're just going to say block on this thing, run blocking. And we're going to assemble our request. So we're going to use that builder, that generated builder, set the name to the name that we got in. And then we're going to use our stub, which is our way to communicate with that server. We're going to call the say hello method, give it our request. And then when we get the response back, which is asynchronous, but because we're in run blocking, it's going to automatically await this result for us and in a non-blocking way. And then it's going to get the message out of the response and output that. So that's our very simple hello world client. Let's run this thing and see it actually work. So there we go. We now see that the greeter got a message back, which was hello world, which came from the server, which combined the string world and a prepended hello in front of that. So very trivial, simple example there, but hopefully that gives you a quick rundown of how to start building a GRPC Kotlin application with GRPC Kotlin. Now, if we did want to go see some of the code that is generated by the protoc plugin, we can come in here and look at our generated source and go into proto and see the different generated targets from our build. There's the greeter GRPC. Here's the Kotlin piece that was generated and some Java that was generated here with our actual value objects, our hello ply, hello request and the builders for those. Okay, so you might be wondering about this builder syntax. That part is not super Kotlin E and it's very Java E. That's how we would do that in Java, but Kotlin has data classes. And so wouldn't it be nice if we could also generate the Kotlin idiomatic version of those data classes as well. And this is actually a project that we're working on for Kotlin protoboss, which will allow us to have a much more Kotlin-y syntax for those pieces that we're currently using in the Java GRPC protobuff plugin for. So that's a project that we're working on and coming soon on that one. Hey, James. Yes, David. We got one question from the Q&A that it might make sense to answer about the demo. So one person anonymous asked, does the GRPC Gradle plugin support the Kotlin Gradle DSL? I can answer that one. It does, but it's experimental and I'll post a link. Awesome. Thank you, Brent. And thanks, David, for chiming in. Good to have you here helping. Okay. So we've got our basic hello world example. And I'm gonna close those down because we're gonna continue on with the next example here, which is a Maven example. And I wanted to show you what the Maven build looks like for this same sort of project. So if you are using Maven instead of Gradle, that's great. You can use the same sort of thing to be able to do this. So let's walk through the pieces here. We've got the Kotlin stuff. We've got the protobuff stuff. We've got the GRPC stuff, just like you've already seen. But then we also have a dependency on that GRPC Kotlin stub version 0.1.1. And so that brings in the dependencies that our generated code need. And then we're using the protobuff Maven plugin. And we're just using the standard plugin for generating our protobuffs. And you'll see just like in the Gradle one, we're using GRPC Java to generate some of the pieces here. And then we also have our GRPC Kotlin, which generates the Kotlin pieces. And so this is from the new open source project that we announced yesterday, the Kotlin plugin. Okay. And then of course we have the Kotlin compiler plugin in here as well. So I won't run through the proto and code on this one because it's the exact same as what you've already seen. But I will share at the end the link to go get all of these samples. Okay. So that's the Maven Palm build. Now let's go on to the next example. And for this one, we are doing streaming. So in this case, we are doing server streaming. So what that means is that we have an RPC, which is say hello stream. And this one takes a request, but then returns a stream of hello reply. So instead of just a single hello reply, it's gonna return a stream of them. So let's look at how this particular one is implemented. We'll start with the server and it is all very similar to the code that you've seen before, but instead of returning a suspend fun with the hello reply like we saw before, in this case, what we're gonna return is a flow of hello reply. So this is using the Kotlin API for streaming for flows. And so we're gonna create a flow. And then my very trivial flow here, all it does is repeatedly once a second emit a hello reply. And that hello reply is very similar to what we saw before. It's just the request, the name and the request and that turned into a low reply. Okay. So that's our server implementation. And then let's go take a look at our client. So for our client, what we're gonna do is, again, we're gonna run blocking because this is just going to do its thing and then exit. And so we're going to create our request just like we did before. But this time, when we call that stub say hello stream and give it the request, what we actually get back is a flow. And so then we can do all sorts of flow operations on this thing. One of the flow operations is called collect. What collect does is every time there is an element emitted from this flow, you can have a Lambda function that then does something. And in our case, very trivially, we are going to just print out that message. So, and then the rest of this looks very similar to what you saw before. Okay. So we've got our server streaming example. And I did wanna show you that I could run this locally but I did wanna show you that this all does work on cloud services as well. And we have a cloud service that runs these great, which is called cloud run. And so in our example, this is the GRPC hello world streaming example, you can deploy this on cloud run and that will create a Docker container, deploy that Docker container on the managed cloud run service. You could also deploy that container on Kubernetes or other places that run containers. And now I've already done that piece and deployed that application. So it's all up and running here on cloud run. And so that's great that it's up and running on the cloud. But now we need a way to run our client to call it. And so I'm gonna go over into Cloud Shell and I've already set this up so that I can just do the Docker run. And then I need to give it the parameter of the server to connect to. And cause it doesn't know about what the endpoint for that server is. So I have to tell it. And then I'm gonna give it my Docker container that I created, which has both the server app and the client app in it. And of course you could create your Docker containers however you want. But I put them both into a single Docker container and then I give it a parameter to say which process I wanna run. And in this case, I'm gonna run the client. And so now that will go get the Docker container, run it with that parameter and it's gonna run that client that we were just looking at. So it's gonna run this client that's going to make that request but get the stream back and then output the result. So let's go over back to our Cloud Shell and you'll see that now sure enough our server is responding with a stream that's emitting a value once a second that's taking in our case the name that I gave it was just the server name that we were connecting to but you'll see that now it's outputting that once a second. So it just will continually stream that output. Okay, so that's our server streaming example and runs great on Cloud Run or other places that can run containers or really anywhere that can run a GRPC server. Let's take a look at our last example now which is taking this a little bit further. So we could also have a service that does client streaming. So in the client streaming example we would take in a stream and then just return a single value. But in this case, we wanna do a show bi-directional streaming where our RPC takes in a stream but then also returns a stream. And so let's look at the server for this one. So our server implements that function that takes a flow of hello request and returns a flow of hello reply. And so I need to somehow do something with this stream and turn it into another stream. And so a very typical way to do this on a stream is to do what's called a map. And a map just says every time an element omits on this request, then do something with it. And so what I'm gonna do is I'm going to print it and then I'm going to create a hello reply and this will just automatically get returned. And so that now has transformed my flow of hello request into a flow of hello reply. And so I've done my stream transformation all the rest of this code looks the same as what you're familiar with. And so this is kind of like a echo service. And so then on my client side, I need to give this thing a flow. So I'm going to assemble a flow and very similar to what you saw before, my flow is going to once a second emit a value which is going to be my hello request. And so now this is a flow of hello request and let's go start up our server, get that running while we're waiting while we go through the rest of this. Okay, so now I have my client and I'm gonna call say hello on my client and give it the flow. And that's going to, and well, let's go see, let's go look at that hello, say hello to see what happens. So this is a suspend function. It runs in a coroutine scope and you'll see that I'm calling my stub say hello stream. I'm giving it my flow of hello request and then every time I get an element a response, I'm just going to print it. So very simple examples here, but hopefully it helps give you the gist of using gRPC with Kotlin. So let's run this one, make sure that it works. So there we go, we see hello world repeating every second. So now that's a bidirectional stream because my client is producing a stream that's going to the server being transformed and then being sent back to the client. And so there we go, bidirectional streaming there with Kotlin and gRPC. Okay, so those are the four examples that I want to show you and I'll give you some information on where to find all those. So this is all in the slides, but we saw the code in IntelliJ. So the best place to learn how to get started is in the gRPC docs. You can go to the quick start for Kotlin and great documentation there. If you do want to check out those four samples that I showed you, those are on the Google Cloud Platform GitHub and then under Kotlin samples and then they're in a directory called run because those are the cloud run examples. So that's where all those examples that you saw are. Okay, so that was what I wanted to show you with gRPC Kotlin and we have some time now for questions and we've got Lewis and Brent and David who will be helping us with those. And yeah, so let's see what questions there are. Awesome, thanks James for a great presentation and demos. That's great. Okay, so I'll go ahead and read the question and we'll go ahead and dig in here. So I'm probably butchering this, but Sharisha asks, I am looking into the GitHub repo of gRPC Kotlin. How can we start contributing to it? I guess you would just answer that through the docs, right? For actually contributing, which of course we would love contributions. We open source this so that we can get everyone involved and making it better. And so that is actually on GitHub.com slash gRPC and then gRPC-Kotlin. And yes, we would certainly love contributions to that repo and so that's where the source code for the stub that I pointed out as a dependency and then the protoc plugin, all the code for that stuff is all in that repo. So certainly would love contributions there. I'd like to chime in right there also and just say probably the best way that you can help is to use the library and give us feedback and report bugs or issues if you find them. And also, of course, fix those issues if you feel so inclined. And we do have a running list of issues on that repo that we'd be happy if anyone wanted to give a crack at. Awesome, thanks, Brad. Great. Okay, the next question is from Subrat. Is it possible to return a file, for example, a zip file less than 1MB as a response from gRPC server? Yeah, let's see. Lewis or Brent, do you have any insight on returning files? I mean, you certainly could just turn the file into some bytes and send the bytes. Is that the best way to do that or is there a better flow or an end way to do file transfers over gRPC? That's certainly how I'd do it. Just with the bytes, turn them into bytes or would there be a better flow way to do it, Lewis? Yeah, yeah, just turn them into bytes. Cool. I think gRPC isn't, single messages might have a cap. I think it's above a megabyte now. But the worst case scenario is you turn it into a stream and concatenate them and send those messages and put them back together on the client, which should work quite well with the flows. That's a good point. I would guess that there is a way to go from a input stream in Java to a flow in Kotlin. That's, I haven't looked in that in particular, but I guess that there is, and if there is, then yeah, you could read your file into an input stream and then turn it into a flow and then stream it. But if anybody wants to try it and if you have problems, then please file issues on the gRPC Kotlin repo with that. Perfect. Tommy asks, what's the biggest difference between Kotlin and Golang? Another great GMB option as it relates to gRPC. What is your number one favorite aspect about Kotlin? If you could change one thing to make Kotlin better, what would it be? So that's like a three in one question. Yeah, for me, I do most of my programming on the JVM and so Kotlin's ability to interoperate with Java and be on the JVM is certainly good for me. There's also a lot of really nice functional programming oriented features in Kotlin that I really enjoy. When I take that really far, there's a functional programming library in Kotlin called Arrow, which is really nice. So there's, for me and my preferences, I really enjoy programming in Kotlin for those reasons, but maybe Brent or Lewis wants to chime in or David with some other anecdotes on that. Nothing immediately comes to mind. I mean, I can think of one or two things I might change about Kotlin to answer the last part of the question. Mostly, better said, static analysis, plugging points, but that's all I can think of. Cool. Brent or David, anything to add? Cool. Nothing over here. Nothing on my end. All right, we'll move on to the next question. Yorosh is asking for containerized applications, how heavy are these dependencies? Was that a 256 MB image built? Yeah, so the biggest dependencies in really any Kotlin application that's on the JVM is going to be the JVM itself. And so I was actually just exploring container layer sizes recently and found that about as small as I could get a Kotlin JVM based application was about 150 megs. A very small portion of that is the generated code and the stubs. And so really a lot of that is actually in the JVM itself. There is a technology that Oracle is working on called GraalVM and GraalVM allows you to take a Kotlin or Java or Scala, any JVM application and compile it down into native code doing ahead of time compilation. And when I did this on, it wasn't a Kotlin project but I think you could find similar effects. I did this on a Scala project recently and my output binary, my output container that can run without any dependencies was 12 megabytes. And so you with GraalVM can get these things a lot smaller. And if you're doing, I haven't looked to see what the GraalVM support for GRPC in particular is yet. There is ways to get it to work with most stuff. It just takes a little bit of configuration where you have to specify any reflection information through a configuration file. But you can generate that reflection, mostly generate that reflection config file using GraalVM. And so something worth looking into. And as we explore that, we will be sharing that on my Twitter and other places as well for doing GRPC and Graal. That's a fun thing that I will be looking into. So thanks for that question. We just had a question related to that come in about Graal. GraalVM would be tough, right? Since GRPC uses reflection, which Graal does not support? So you can do reflection, you just with GraalVM, you just have to have a configuration file that specifies the reflection information. And so I did this on a project recently and let me actually go see if I can show it to you. So this is a project that does a lot of reflection. I think there is even some protobuf stuff in this particular one. And so if we go look at, I'll show you the, I'm gonna get the configuration file for the reflection. You'll see that it is a little, this is all the information that specifies all the reflection that happens. And so it certainly was possible and most of this file was generated for me, but there were a few places where I had to to do some manual tuning of this file to get it all working correct. So you'll see there's some com Google comments and stuff in here that was being used. So it is possible. I haven't done it on a GRPC application yet, but I will give it a try and report to back on my Twitter, which on Twitter, I am underscore James Ward. So follow me there if you are interested in updates on that. Okay, next question. Weedow asks, in server mode do tools like VertX, Spring, Quarkus support GRPC? There are some of those other server frameworks that do speak the GRPC protocol. I think most people that are doing GRPC are just using the out-of-the-box GRPC NetEase server. But yeah, there are other frameworks that do speak GRPC. So I don't know specifics on that. I don't know if anybody else knows more about that. Brent or this? Or AP? It hit me down. Okay, next question from Peter. Does GRPC support back pressure? Sorry, using this Flow API. Yes. Yup, yeah. So because it's built on Flow, it does support the back pressure in the Flow API. I was honestly when designing it, I was really pleased with how well that just worked together with out-of-the-box pretty much, which really improved my impending of the Flow API. Yeah, but it just worked, is pretty awesome. Okay. Suresh asks, since Kotlin X serialization supports ProtoBuf, can we use serializable data classes directly, assuming my client and server in Kotlin? An apologies if I butchered that name. Serializable, I think. Serializable. Sorry, guys. Yes, so could you pass data classes around today? The answer is no. And I think part of that is the last time I checked on Kotlin X serialization, when it says Kotlin X serialization supports ProtoBuf, what they mean is that they support using ProtoBuf as the serialization in terms of how the message is stored, sent, converted into bytes, but not necessarily in terms of how the message is defined, in terms of how the message evolves, and certainly not in terms of the ProtoLanguage.ProtoFiles or anything, that's what's currently supported. So Kotlin X serialization doesn't currently work. I know GRPC supports other serialization mechanisms and that could be made to work. We have that working with Kotlin at this time. Cool, thanks, Liz. All right, it looks like we have one more question here. Malakai asks, how well does it work with Android, specifically thinking of the conflicts between GRPC Java for any timestamp and Java Lite used by Firebase, which conflict with each other? That's a good question. David, would you happen to know that one? I actually don't, curious to hear if Lewis and Brent have any thoughts on this. I can take a shot, which is to say, I'm not specifically aware of the nature of those conflicts, but I would be shocked if the situation was any different from GRPC Java, which is to say, currently, this GRPC... I'm going to lose you, Lewis. Well, I'll take a crack at what I think he was about to say, which is that this Kotlin library is built on top of GRPC Java, so it's going to have the same conflicts as GRPC Java has for Android. Cool, hopefully that's something that we can improve over time and so track us on the GitHub for... I don't know if there's an issue for this already, but if not, we should create one and you can follow, subscribe to that issue, so... Yeah, I think it's a 0.1.1 version. We're not sure how this works in the Android environment, but I personally have a project in-flight, try to test it out, so... Also, yeah, it'd be great to be able to talk from an Android client to a GRPC server, so hopefully that's something we can get working, so... Okay, I think that's it for the questions. Christy, what else? Yeah, thanks again for a great webinar. Thanks, James, for presenting and to David, Lewis and Brent for answering questions. And thanks again for everybody for joining us today. A friendly reminder, the slides will be available and the recording of the webinar on the CNCF webinars page later today. And thanks again, have a great weekend and we look forward to seeing you at a future CNCF webinar. Thanks, everyone. Bye.