 Good morning, everyone. Let's get started. Thank you, you at night for following me on Twitter already. Hi, I'm Shubham. I work at Microsoft and I'm a maintainer of Dappers JavaScript SDK. So this morning, we'll talk about how we can write high throughput applications using Dapper. So we'll start with seeing how some asynchronous messaging patterns work in general, and then how you could use Dapper with your PubSub needs. Then we'll look at the newest addition to Dapper's PubSub block, double KPIs. So yeah, let's get started. Okay. So this is how a normal asynchronous messaging application looks like. You have your publisher, you have a few subscribers, you have a Kafka PubSub broker in this case. So your card can publish events onto Kafka, and then Kafka sends those events to the subscribers. In this case, it's shipping and notification and they can do something with that event process that and so on. You could also have an alternate model where you have multiple publishers. So you have multiple publishers who generate events, send it to Kafka, and then you have a single subscriber listening for those. You could have MS2 and any combination basically of publishers and subscribers. You could also do something like this, where you have multiple replicas of the same subscriber, and then each replica helps in load balancing your events. So if you have, imagine if you have a lot of events, right? And you want to process them. So you could have multiple replicas doing that on your behalf. So for those of you who don't know about Dapper, Dapper sits as a sidecar, and let's see how you could do a simple operation like publish and subscribe using Dapper. So each application has its own Dapper sidecar in the scenario. And what the cart application needs to do is send this request to Dapper. So Dapper is a sidecar on the port 3500. It's a well-defined port. And all you need to do is do a publish slash Kafka slash order. So publish is the operation. Kafka is the name of your broker. And order is the topic name. And then you could send your data. And Dapper will then send this data to the PubSubQ. It'll find where Kafka is. It'll send the data to that. It'll listen for data from the PubSubQ as well. And then forward those data to the subscribed applications. And in this case, Dapper can use any components it supports. So on the top, on the bottom right, you have a list of supported components. And Dapper supports a lot of PubSub components today. So you can check it out and note that your application does not need to change. So your application does not know if it's Kafka, if it's RabbitMQ, if it's RedisCache. All you need is this URL right here. Can I use a pointer? Yeah, this URL right here. So the PubSub name can be parameterized or you could just manage it on your own. And then Dapper knows how to talk to SNS or Kafka or Azure Service Bus. So let's look at some of the highlights of the Dapper's PubSub ecosystem, right? So Dapper ensures that you have at least once delivery. It ensures that each application that is published is also delivered to your subscribers at least once. It has some concepts of consumer groups. So Dapper automatically creates consumer groups on your behalf. It also allows you to have a competing consumer group patterns and we'll look at these patterns in our demo really quick. You also have built-in resiliency. So with Dapper when you go out to do PubSub operations, you get retries, timeouts, circuit breakers out of box. So this is how it looks like, right? So you get resiliency on both the inbound and outbound part of things. And yeah, so Dapper talking to publishing data on to the broker and then Dapper sending data to your subscriber, it's all resilient. And then even after resiliency, you get a dead letter topic support. If your resiliency policy is still failed to deliver some message, you have built-in support for dead letter topics. And finally, you have bulk messaging which is recently introduced and we'll talk about that in the future slides. Also note that on the right side, we have something called a component spec. So this is how you tell Dapper that, hey, I have a Redis component which is of type pubsub.redis and it's located at this host. This is the password and that's how you connect with it. So that's all you need to connect to Redis and you could just swap it out with a different component. In this case, it's an Azure Event Hub component and the metadata changes and that's it. So that's all you need to do to tell Dapper how to talk to these components. So yeah, let's look at a really quick demo. Let me just stop sharing this presentation. Okay, I hope you can see it. I'll zoom it in a bit. Yeah, okay. So we'll just start looking at really simple operations, how to publish some data onto PubSubBroker, how to subscribe to that data. So for publishing, let's look at the HTTP way of it first. So I'm not using any Dapper SDKs. We'll look at that in a bit. But if you want to post the data to Dapper, all you need to do is have this URL. So in my demo, in this case, Dapper runs as a process locally in my computer. So I could just go and tell, hey Dapper, just publish this data onto a PubSub called PubSub. So just if you install Dapper via the Dapper CLI, you get this PubSub component inbuilt and Redis is already spun up for you. So all you need to do is talk to this PubSub component and let me show you how it works. Let me just, so what I can do is I can just start a, I can just start a publisher app like this. So I have an app ID and I have a Dapper HTTP port, which is 3,500 in this case. And I just want to start my Dapper. And I don't have any application in this case. It's just a sleep. And we'll just use the HTTP call directly to talk to Dapper. So yeah. So this has, like my Dapper is now up and running. And what I can do is just send this request. So it says 204 no content, which means that it was able to publish everything. And what I have is a GUI. Oh, it's this one. Yeah. So let me open that really quick. And I can connect to my local Redis. And now I can see this data that's being published already. So let me just show you in a different tab here. So if I format this. Yeah. So if you see, this is the data that got published and it's a cloud event. So what I published was this actual data and Dapper talks in cloud event by default in PubSub. So this is a cloud event. You have things like trace ID or trace parent, the cloud event ID itself and so and so. So this is a simple example of publishing. And I could also like not use a cloud event. So in case you have subscribers that don't speak in cloud events, you could go ahead and just publish without it. So all you need is this extra query parameter called raw payload is true. And if I send a request like this and just refresh this. So this one is plain JSON. There's no cloud event here. So yeah, that's how you could like subscribe. And if I also want to, that's how you could publish. Sorry. And if you also want to subscribe, I have a simple subscriber application written here. This is in Python and there's no extra dependencies. It just uses the default Python web servers. And there are two important parts. First one is this, which is dapper slash subscribe. So whenever dapper starts, right, it goes to your application and it goes to the same point and ask, hey, application, which subscriptions do you want to subscribe to? And what I respond with is this. So I have a PubSub and the topic name is examples and this is the route. And dapper will then start sending me messages on this route. In this example, I'm just printing that out. So that's it. And I'm sending back a success status. You could also send back a, you know, maybe a retry or a drop to dapper. But yeah, in this example, I'm sending a success. So let me just try to run this one really quick. So app ID, yeah. So the protocol is STTP, the port on which my server is running is 3000. And the command is just Python subscriber dot pi. And yeah, there you go. So a few things first. So if you look at it, first this dapper slash subscribe was called and then dapper says I'm subscribed to these topics. So the topic name is examples and PubSub name is PubSub. And then I receive these messages. So this is the one without cloud event and this is the one with cloud event. So yeah, this is how simple publishing subscribe works. Let me stop these and minimize this. So let's also look at another example where I'm using the JavaScript SDK. So you saw how to do it using STTP but directly. But if you want to use SDKs, it makes a life simpler. So let's look at this example. We have two applications. One is the publisher service. If you look at package.js on it just has one dependency called dapper. There's no other dependency on any Kafka SDK or any latest SDK, but there's only one on dapper. And this is my index.js file. So what I do is I create an infinite loop. I create a random order and I publish it. So this is the only thing I need to do. PubSub.publish name, PubSub name, topic name and the actual data. So that's it. And let's also look at the subscriber application. Same for this one. So there's only one dependency dapper. And if you look at the index.js, this is a really an upgrade from the STP version. So if you look at this, all you need to do is have a subscribe method and this is a callback. So this actually, this is a place where you get your message. So in this example, I'm just logging it out, but you see there's no dapper slash subscribe. The SDK takes care of that for you. So yeah, it's really simple and easy to write your own SDK version of it. And so in this demo, I'll be running these on Kubernetes. So I have a local kind cluster setup. And if I show you, I should have, let me zoom out a bit. Yeah, so I have the dapper control plane already up and running. I have Kafka and Redis applications here running and this is a cube system. So let me just try to really quickly deploy my sample application. So first I deployed the Redis component and this is nothing but a configuration that shows up here. And I can also show you that it just, so this just points to the Redis that exist in my cluster already. So the host and password, and this is coming from a secret store, by the way, instead of using a password directly. So that's it. And let me also apply the publisher and subscriber configurations now. So these are Kubernetes deployment specs. And so there we go. Sorry. Yeah, so yeah, they're up and running. We should be able to see them here now. Yeah, okay. This is up and running. So let's look at the logs and see what's happening inside. So if I go and do a cube tail, let me just split this. So yeah, the publisher is up and running. It's already publishing data already. And if I look at the subscriber, oh my bad. Yeah, so you see that the subscriber is receiving data and publisher is publishing data. This is running on Kubernetes. Great. Let's look at a few more things, right? So what I can do is I can scale up my number of replicas. So let me do something like, so what I want to do is have three replicas for my subscriber application, and let's just do that. So it got scaled up. And if I now go to, if I now start this logging again, I'll be able to see my replicas. So my replicas are starting now. And you could see that they each got a subscription up and running. So now these replicas should load balance my events. So if you look at this, different replicas are receiving events now. And that's as simple as that. So when you have different replicas, Dapper automatically load balances the load for you. And in parallel, I could also start another subscriber service identical. So if you have, let me show you really quick. Yeah. When you have a new subscriber running, it behaves as a queue basically. It behaves as a topic and it sends the same event to both the subscribers. So I could just do this. And this is getting all the events now. So both the subscribers are getting their own events basically. And the one with multiple replicas is getting load balance. So yeah, this is how like, this is the competing consumers pattern where you get different, where different replicas get one message each and then the consumer group pattern where different application IDs get the same events. So yeah, this is how it works. And now what I can do is a really interesting thing that people often do. Say I want to use Kafka instead of Redis. All this while I was using Redis. But if I want to use Kafka, all I want to do is apply a Kafka spec. So now if I see, I have two components Kafka and Redis and I can remove Redis for that matter. Let me go ahead and do that. Yeah, not everything, but just the Redis one. And I have deleted Redis now. So what I want to do is just let my applications know that, hey, the name of the PubSub has changed and instead now use Kafka. So earlier they were pointing to Redis and now I have it updated the environment variable to use Kafka. So that's all I need to do. And if I look at the logs again, right? By the way, it should have already restarted my application. So if I look at the logs again, yeah. Okay, so now this is running on Kafka and listening to everything and same for publisher. I can just do this cube tail and it's publishing on Kafka. And how I can confirm is by taking this ID and going in like this. So yeah, if you can see this API call, this is going to Kafka. So it's as simple to switch components. You don't need to learn how Kafka works. You don't need to learn how Redis works. All you need to do is just let Dapper know I want to talk to these components. So yeah, that was it for the demo for now and let me jump back to this. So yeah, now we can look at some of the new improvements that we introduced in the Dapper PubSub block and two major parts of it. So first is the API itself. So using the bulk API, what you can do instead of publishing events one by one, you could publish like a bulk of events to Dapper. And then what Dapper does is it forwards the events in parallel published request to the PubSub queue. Similarly, while receiving, Dapper will receive events for you, buffer them for some time and then send it as a bulk to your subscribing app. And this improves the latency between the application and the Dapper by a lot. And also Dapper can make parallel published request now, which is even faster than making a single request. Furthermore, if the PubSub component also supports bulk, you could also optimize it further. So the Dapper sidecar can also talk to the broker as in bulk request. It can be on the publish end, it can be on the subscribing end. Just to give you some highlights, it's available as an alpha API from 1.10, which will release a few weeks back. It's highly performant because now you have optimized Dapper to application, as well as Dapper to broker communication with the bulk APIs. It's non-transactional. So if you are sending, say, 1000 messages, maybe 990 can fail and 10 can pass or the other way, right? So what Dapper does is it lets you know which one failed and you can retry them. And there's no guarantee for any ordering if you're sending 100 messages in bulk. They can be received in any order and sent in any order. And what you have is unique IDs by which you can identify your messages. So yeah, that helps you. And let's really quickly look at how the PubSub, the bulk APIs work. So let me, I'll just minimize this. Yeah, so like the HTTP example for PubSub, I have this bulk API here. So it's a simple HTTP request. So this is an alpha API. It uses publish slash bulk. And this is the name of the PubSub as usual. This is a topic name. Now notice the content. And in here, I have an entry ID. This is the unique message ID I need to tell Dapper for my message. And this is the actual message and the type, the content type. So I could send text as plain or JSON or cloud event, whatever I want. And let's just spin up a Dapper sidecar. So I just copy this command. And let me try to run this. So it says 204, no content again. And it should have published both the messages. And I have my medis GUI to check that. So I got this new topic bulk examples. And these are the two messages. So this is the first message, this is the second message. And I got both of them in my PubSub queue. And I could also publish without a cloud event. So I could just send this. Again, I got a 204. And if I refresh, I got two more messages. Now they are not wrapped in cloud event. So this is how the bulk publish API works. And similarly, I won't be running subscriber, but you can just check how easy it is. It is exactly the same as the other subscriber. But you have a bulk subscriber enabled true. So this tells the dapper that receiving bulk. And then when you actually get the message, you have to kind of parse it and figure out where's the entry ID event and what are the status of the, and respond back with some statuses. And this is where the SDK comes into picture and makes your life really simpler. So if I go back to my demo. So this is the only change required to start using the bulk API via the JavaScript SDK. Earlier, we would be publishing it one by one. And now we'll do a publish bulk. That's it. Instead of sending one message, you send a bulk of messages and JavaScript API, it's really simple. For subscribe, it's even simpler. So if you notice this, the only changes, the subscribe method is now called subscribe bulk. The callback also remains the same. So in the callback, you get, you still get messages one by one, but behind the scenes, it's optimized for you. It's being received in bulk. So yeah, that's the best part. So your callback, your callback remains exactly the same when you migrate to using bulk API. And we did, we did try it out. We did see how fast it is. And it was phenomenal for publish. You could already see the P95 became 55 milliseconds from 176 in this performance test. And all these tests are on the dapper repo. You wanna go and check. And for bulk subscribe, again, we did a similar test and you could see the latency really dropping the P95 and P50. And this is a graphical comparison if you want to look at it. And yeah, that was for my talk. And thank you so much. And you could go and check out the dapper repo here and join our Discord community. Follow us on Twitter. And yeah, we have community calls. You can come and check out those as well. So here are some links and here's the QR code for the presentation. I'll be sharing the presentation as well as the code shortly. Yeah, on schedule, schedule app. Yeah, thank you. Any questions? Yeah, sure. There's a mic. You can take the mic and ask. Yeah. All these features that you present are also available in Azure container apps. Yeah. Okay. Are you in the dapper integration on Azure? Yes, yes. Yeah, it is. Second question is, are you planning that I can use, I can replace Kafka completely and use a pop-ups pattern with dapper all together? Yeah, in theory you could. So unless you are extremely invested in Kafka, unless you are doing really specific fine tunings, you could come and use dapper. And there have been users who have reported really great performance with the bulk API, comparing it with the Confluent SDK directly. And it was really great. So yeah, you could. And but if you are really into Kafka, then probably you're better with Kafka. Thank you. Yeah. Thank you for the enlightening talk. You mentioned that there is no in-order processing. Is it also true if you're ascending to the same topic or is it a general theme? Yeah, it is in general. Like the order is not preserved in bulk APIs. Yeah. Thank you. I was wondering if you can use the bulk functionality separate. So for example, if you can use the bulk publish, but still use the single consume part. Yeah, you can do that. Absolutely. Those are orthogonal APIs. So you could use mix and match. Okay. Yeah. Okay, thank you. Sure. Thank you. Who is acknowledging the messages or in case of Kafka committing the offsets? Especially in the case of bulk APIs. Thank you. Yeah. So the application is responsible to respond back with the status. And then dapper responds back to Kafka and that's the actual checkpointing. So without that, Kafka will keep retrying and dapper will again keep retrying. Yeah. Any other questions? I have a question about it. Non-transactional problem with the bulk messaging. Yeah. So that is non-transactional between dapper and Kafka or dapper between the other dapper that we're communicating with applications. So generally when your application is talking to your dapper instance, right? It is non-transaction. Now the underlying implementation might vary. In some case, maybe the underlying implementation is transactional, but on the dapper to application level it's always non-transactional. So that's for the application like to deal with it. Yeah. Okay, thank you. Yeah, sure. Like one example of this is maybe, I don't know if it's GCP batch or something but there's one component that only has transactional support. But still dapper treats it as non-transactional because of the API. So the API does not guarantee that. Yeah. Hey, on the bulk consumption, does it also split it like in chunks and you can have multiple consumers at the same time or the entire bulk is just given to one of the consumers? You could have multiple consumers. So you could again go back and have this pattern somewhere. So you have multiple consumers and both of them are getting their own bulk. Yeah, but if I publish a bulk, let's say we have a hundred elements inside of it. Will it be a hundred elements given to only one of the replicas or does it somehow split the bulk? Yeah, if you have different replicas, then yeah, you will get different bulk for each replica. Like different set of messages for each replica. You have to load balance. The bulk is split in chunks too and given somehow load balance to all of them. That's correct. So when you do a bulk publish, that bulk gets published and then that's it. On the subscribing, it's a fresh set of messages in a bulk. Okay, it's a subset of that. And on the, when we publish it and if the set is too high, what should we take into considerations? Exactly. So there are some metadata that you could see and each component has its own maximum bulk limit. So that way you'll have to figure out what component you are using. And there's also on the subscribing and also there are some parameters you could find too. So yeah, it depends on what component you're using. I got it. Thank you. Sure. Any other questions? Okay. Thank you folks. Thank you. Thank you for listening.