We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Performance is not an Option

00:00

Formal Metadata

Title
Performance is not an Option
Subtitle
Building High performance Web services with GRPC and Cassandra
Alternative Title
Performance is not an Option- Building services with GRPC and Cassandra
Title of Series
Number of Parts
96
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
In today's development ecosystem building a service oriented architecture based on a micro services is common practice. With the rise of Big Data and Internet of Things applications making these services highly performant services is no longer an option. In order to accomplish the scalability and performance requirements that customers expect we are required to start thinking differently about how we architect and build these applications in order to meet those demands. This session will demonstrate a method for creating a highly performant service based application using Google’s GRPC and Apache Cassandra in .NET. We will show how you can combine gRPC to minimize communication overhead while leveraging Cassandra to optimize storage of time series data. We will explore these concepts by creating an Internet of Things (IoT) application to demonstrate how you can effectively meet the performance and scalability challenges posed by these new breeds of applications.
BuildingComputer configurationSeries (mathematics)ArchitectureStrategy gameProduct (business)Expert systemTime seriesPhysical systemBitEndliche ModelltheorieSoftwareTheory of relativityArchitectureClient (computing)Software developerProcess (computing)Product (business)Strategy gameSupercomputerVariety (linguistics)Complex (psychology)QuicksortWeb applicationEnterprise resource planningComplex numberSequelService-oriented architectureImage resolutionRow (database)Computer animation
Remote procedure callSoftware frameworkStandard deviationOpen sourceFreewareSystem programmingClient (computing)ArchitectureLocal ringScalabilityService-oriented architectureProgrammable read-only memoryData modelMessage passingString (computer science)Code generationMessage passingEndliche ModelltheorieServer (computing)Client (computing)Formal languageVariety (linguistics)Interface (computing)Open sourceFile formatElectric generatorEnterprise resource planningArchitectureService-oriented architectureMathematical optimizationOnline helpBitDependent and independent variablesQuicksortBand matrixSystem callWindowNumberObject (grammar)Java applet.NET FrameworkPhysical systemStandard deviationSoftwareGame controllerSoftware frameworkDataflowData compressionWeb-DesignerNatural numberMobile appCASE <Informatik>CodeRemote procedure callScalabilityConnected spaceMultiplicationCartesian coordinate systemTDMACommunications protocolBuffer solutionDefault (computer science)Web serviceObservational studyDifferent (Kate Ryan album)InternetworkingAuthenticationOpen setRepresentational state transferMereologyStreaming mediaCategory of beingBinary codeInsertion lossMultiplication signGoogolHeat transferMobile WebInternet der DingeFreewareComputer wormSinc functionComputer animation
ScalabilityOpen sourceSingle-precision floating-point formatPoint (geometry)Partition (number theory)ConsistencyTheoremSpherical capVertex (graph theory)Computer networkPhysical systemDependent and independent variablesPermanentDatabase transactionAtomic numberComplete metric spaceState of matterArchitectureToken ringRing (mathematics)Hash functionReplication (computing)DivisorNumberBitUniqueness quantificationSet (mathematics)PhysicalismPower (physics)Single-precision floating-point formatServer (computing)DatabaseDatabase transactionOrder (biology)Open sourceCASE <Informatik>MereologyPoint (geometry)Ring (mathematics)Token ringCartesian coordinate systemHash functionSoftwareMultiplication signDrag (physics)ArchitectureResultantRange (statistics)Arithmetic meanACIDPairwise comparisonHierarchyGroup actionData storage deviceNumberCategory of beingState of matterComputer hardwareData centerMiniDiscAlgorithmCoordinate systemReading (process)Volume (thermodynamics)TheoremPartition (number theory)CodeSpherical capProjective planeKey (cryptography)ConsistencyReplication (computing)Physical systemSoftware testingComplete metric spaceDependent and independent variablesSequelRelational databaseMoment (mathematics)Client (computing)Right angleDivisorMultiplicationSemiconductor memoryCoprocessorService-oriented architectureScalabilitySpacetimeComputer animation
Replication (computing)NumberDivisorGroup actionConsistencyLevel (video gaming)Basis <Mathematik>Query languageTerm (mathematics)ArchitectureScalabilityApache CassandraData managementService-oriented architectureSoftwareIntegrated development environmentInformation securityEnterprise architectureSoftware maintenanceDatabaseSmoothingFunction (mathematics)Expert systemData centerTheory of relativityReplication (computing)Endliche ModelltheorieLevel (video gaming)Reading (process)Cartesian coordinate systemOpen sourceOrder (biology)ConsistencyNumberMultiplication1 (number)Overhead (computing)Server (computing)Database transactionCASE <Informatik>Dependent and independent variablesDifferent (Kate Ryan album)WritingRight angleMereologyMultiplication signEnterprise architectureData storage deviceProjective planeRelational databaseFreewareQuery languageFunctional (mathematics)Product (business)Information securityData managementACIDClient (computing)DivisorINTEGRALPartition (number theory)Subject indexingSynchronizationAdditionPoint (geometry)Revision controlBitSingle-precision floating-point formatAnalytic setCoordinate systemVideo gameReal numberBasis <Mathematik>Buffer overflowStack (abstract data type)Term (mathematics)Operator (mathematics)Key (cryptography)Task (computing)Directory serviceLocal ring2 (number)Gene clusterGodNatural number10 (number)Scaling (geometry)Complex (psychology)ArchitectureDigitizingSinc functionApache CassandraToken ringAutomatic differentiationGroup actionComputer animation
Apache CassandraIntegrated development environmentInformation securityEnterprise architectureService-oriented architectureDatabaseSoftware maintenanceSmoothingData managementFunction (mathematics)Expert systemSoftwareSQL ServerClient (computing)MeasurementArchitectureManufacturing execution systemQuicksortEndliche ModelltheorieRelational databaseBitProduct (business)ArchitectureStructural load2 (number)Integrated development environmentDesign by contractMereologyInformationAdditionWeb 2.0Reading (process)MeasurementDatabase transactionDatabaseClient (computing)MathematicsServer (computing)Set (mathematics)Scaling (geometry)Service-oriented architectureMultiplication signSequelPhysical systemComputer animation
CodeService-oriented architectureMathematicsSQL ServerDatabaseData modelServer (computing)Client (computing)Table (information)SpacetimeKey (cryptography)Device driverOpen sourceApache CassandraCodeCASE <Informatik>Server (computing)Functional (mathematics)Point (geometry)Physical systemSingle-precision floating-point formatEndliche ModelltheorieDatabaseScalabilityMathematicsClient (computing)Computer fileKey (cryptography)Stack (abstract data type)Order (biology)System callPartition (number theory)Object (grammar)Communications protocolCategory of beingDependent and independent variablesMultiplicationAddress spaceStreaming mediaUniform resource locatorDevice driverService-oriented architectureData integrityReplication (computing)DivisorSocial classData centerStrategy gameRelational databaseNetwork topologyTable (information)Context awarenessSpacetimeDifferent (Kate Ryan album)Basis <Mathematik>Set (mathematics)MereologyTheory of relativityBuffer solutionMultiplication signTimestampBitStructural loadInternet service providerFunction (mathematics)TouchscreenInterface (computing)Complex numberNumberFlow separationWindowOpen sourceCellular automatonDatabase transactionSequelIntegrated development environmentCartesian coordinate systemSoftwareBetti numberGraphical user interfaceRow (database)Intelligent NetworkSubject indexing.NET FrameworkComputer animation
Device driverOpen sourceApache CassandraFile formatCodeTask (computing)SynchronizationFluid staticsClient (computing)WindowBefehlsprozessorVideo game consoleElectronic mailing listError messageFunction (mathematics)BuildingComputer fileView (database)Online helpElectric currentInterior (topology)Random numberPoint (geometry)Thread (computing)Configuration spaceTask (computing)Equaliser (mathematics)Streaming mediaOrder (biology)Client (computing)Constraint (mathematics)ArchitectureServer (computing)InformationCartesian coordinate systemCASE <Informatik>CodeKey (cryptography)2 (number)Partition (number theory)Computer fileBitMessage passingDependent and independent variablesInequality (mathematics)Gene clusterAdditionLine (geometry)Row (database)Multiplication signTheoryUniform resource locatorSystem callMultiplicationPoint (geometry)AverageStructural loadDemosceneSingle-precision floating-point formatLevel (video gaming)Table (information)Functional (mathematics)Limit (category theory)Insertion lossDevice driverToken ringWritingReading (process)DatabaseQuery languageSequelStatement (computer science)ResultantMereologyExtreme programmingData storage deviceSource codeComputer animation
BuildingWindowGroup actionView (database)Function (mathematics)Electronic mailing listError messageExtension (kinesiology)Core dumpLibrary (computing)Computer fileVirtual machineReading (process)CodeAveragePoint (geometry)Server (computing)Multiplication signRoundness (object)Data centerSocial classBitNumber.NET FrameworkComputer animation
Web browserType theoryTable (information)Kolmogorov complexityRelational databaseQuery languageBitEnterprise resource planningRevision controlBuffer solutionCommunications protocolLimit (category theory)Message passingWritingMereologyData modelImplementationPerspective (visual)FreewareOrder (biology)Complex (psychology)Relational databaseNatural numberSound effectSubject indexingBeta functionIntegerMultiplication signWeb browserGraph (mathematics)Wave packetLink (knot theory)Software developerFigurate numberQuery languageKey (cryptography)Table (information)Point (geometry)Functional (mathematics)Goodness of fitComputer fileClient (computing)Product (business)Enterprise architectureCartesian coordinate systemSpacetimeEndliche ModelltheorieNormal (geometry)Maxima and minimaData storage deviceStack (abstract data type)Physical systemData typeComputer animation
Service-oriented architectureWindowWechselseitige InformationFacebookConsistencySoftwareOrder (biology)Key (cryptography)Roundness (object)Classical physicsPartition (number theory)State of matterInformation securityDevice driverWeb serviceData modelScalabilitySeries (mathematics)Endliche Modelltheorie1 (number)Absolute valuePairwise comparisonEqualiser (mathematics)Limit (category theory)WritingDivisorReplication (computing)Level (video gaming)Internet forumTime seriesIndependent set (graph theory)Reading (process)Sound effectQuery languageLatent heatDatabaseMiniDiscTable (information)Enterprise resource planningAddress spaceGeometryComputer animation
Open sourceDevice driverApache CassandraService-oriented architectureLastteilungInterface (computing)Mobile appEndliche ModelltheorieWindowServer (computing)Device driverEnterprise resource planningRight anglePoint (geometry)CASE <Informatik>Core dumpVideo game consoleComputer animation
Transcript: English(auto-generated)
Hello, everyone. How's everyone doing today? So, before I get started here, I just kind of was wondering how many people here have ever heard of GRPC? One? Two? Anybody ever used it? One. What about Cassandra? Anybody ever used that or heard of it? What about
other NoSQL solutions? Okay. So, what I hope you take away from this talk is, first off, a little bit of understanding about what GRPC is. Also, a little bit of an
understanding about what Cassandra is. And then how you can build a simple GRPC-based microservice. And how you might be able to persist time series data into Cassandra with using that GRPC-based microservice. And then the last little bit is why you might want to use GRPC and Cassandra instead of more traditional rest and relational model
for persisting time series data. Mainly around why it's more performant and has better throughput. So first, a little bit about myself. My name's David Beckberger. I'm a senior architect at Expiro. I'm from the United States. I live in Houston, Texas. I've been developing
all sorts of software for about 16 years now. Everything from embedded C systems through high-performance web applications to highly distributed systems. I'm also a certified architect on Apache Cassandra and a data stack certified architect. I work for a company called Expiro. What we do at Expiro is solve problems in complex domains, specifically around big
data, IoT, and high-performance computing. We do that across a variety of industries such as financial services, oil and gas, manufacturing, anything like that. My company is broken up into three separate practices. We have an architecture and development practice where we actually build the stuff. We have a user experience practice which does an excellent
job of figuring out workflows and how to show all this complex data in a simple way. And we have a product strategy practice where they actually figure out prioritization and how to actually get this out to the customer base quickest. Here are some of the clients that we have, the companies that we have worked for.
So, first thing I want to talk about is what is GRPC? Well, GRPC is a general-purpose RBC framework. It was built by Google. So I don't know if you guys know this, but Google is highly based on a microservice-style architecture. So they have a microservice-style
architecture. For many years it ran on their own internally built system called Stubby. Stubby was very closely tied to their internal infrastructure. And as they moved into more of an open standard IoT mobile application, it was no longer sufficient because
it was so closely tied to their internal infrastructure. Well, to solve that they decided they wanted to move more towards a standards-based protocol. So that standards-based protocol became what is now GRPC. GRPC is based on the HTTP2 protocol for its transport protocol.
So it's available across any common network infrastructure, which includes many mobile devices. It's free and open source. And it's built specifically for the problems of distributed systems. So if you've ever worked in highly distributed systems, you've probably run into the posting on the Internet of the fallacies of the distributed systems.
Some of those fallacies are, you know, networks are always available. There's zero latency. Bandwidth is infinite. All these sort of problems where if you actually start to build a distributed system, you quickly run into them. Well, GRPC is built to handle some of those sort of architectural challenges. Well, a little bit about GRPC's architecture.
GRPC's architecture, it's a remote procedure call architecture. And it's built to allow clients to call methods on servers as if they were local. So this is very similar to if any of you, I'm sure at least some of you are familiar with SOAP services and have used them before in your applications, where you are able to actually generate a
client from a WSDL, and you will get a client on your side that you can call without having to set up, you know, the network traffic. You don't have to make the request. You don't have to what happens if the request actually fails, you know, handling packet loss, things like that. Well, GRPC is built on the same basic idea, where you can, where
you're able to call the method as if it was a local client, so it simplifies the code that you have to write. It's built for writing low latency, highly scalable microservices. And it's built to be payload agnostic. By payload agnostic, what I mean is by default, the default message format for transferring data to and from GRPC is protocol buffers. Protocol buffers is another standard that Google has for highly
performant message, it's a highly performant message format for passing it back and forth, partially because it's highly compressible. Well, that's the standard that comes with GRPC, but it's also built in such a way that if you have, say, a service that needs
a binary compression algorithm, or it had, you want to transfer XML or JSON or thrift or Adam or any one of the many other different message formats out there, you can just basically replace the protocol buffers portion with the portion for XML or JSON, and you can go. GRPC is also built with bidirectional streaming,
and by bidirectional streaming, I mean that means you can stream data from the client to the server and from the server back to the client, which coming, you know, my background is actually quite a lot of my time has been spent building REST-style APIs, which, when all of a sudden you realize you can stream data back and forth, it actually allows you to think, it allows for some interesting differences between how you would
build a REST API and how you can go out and build a GRPC-style API. And GRPC is pluggable and extensible. Maybe you need a different authentication method, you want a different method for compression, something like that. Well, it's built in such a way that you can just plug in those different methods to replace what's currently,
what the current defaults are and go from there. Well, the first thing when it comes to GRPC is the simple model and service definition. So what you're looking at here is actually a simple model and service definition for a service called hello service. Well, the first two things you'll see there are
the two things that are named message, hello request, and hello response. Well, what that is, what you're looking at is this is actually protocol buffers that you use to define your models and your services. So in protocol buffers, a message is essentially an object, is what will be translated into an object. So you have an object called hello request and an object called hello response.
Each of those has a single property. And one of the unique things you'll notice about protocol buffers, if you've never actually used it before, is that each property has to have a number associated with it. This is because part of the way it does data compression is when it sends it over the wire, instead of sending greeting as part of the message packet, it actually just
sends the value one. Since both sides need to know what the format of the object R is, both sides are able to basically translate that back and forth. So that's why that works that way. If you look, so after your two messages, you'll see there's a simple definition of a service.
In this case, it's called hello service. And it basically takes, it sends out a request, a hello request, and it gets back a hello response. So pretty straightforward. Secondly, GRPC is optimized for speed and performance. GRPC has several things built into it natively that help optimize speed, performance, and
actually help optimize bandwidth. It has things like natural message compression, flow control for the message pass, as well as multiplexing of connections. So by multiplexing of connections, if you say are working in a rest style architecture, if you need to make multiple requests, you end up opening a request, making a request,
closing it, opening, making a request, closing, things like that. With GRPC, you can actually have a long running persistent connection, which you'll send multiple requests and possibly to different end points across that single connection. So it helps optimize the speed because you're not making multiple connection and disconnection requests, and it optimizes the performance because you're there.
It also optimizes bandwidth, which is especially important if you're looking at doing a mobile application. And the last major feature that I really enjoy of GRPC is the code generation aspect of it. So by code generation, you've seen earlier how we defined the models and the service.
Well, GRPC comes with a tool called Proto-C. Well, that tool, what it does is it allows you to generate client stubs and server interfaces for a variety of language. I believe currently there's eight supported languages, but being it's open source, you
can work with any of those other languages. This is sort of similar to how SOAP and WSDLs used to work, except for the fact that you don't run into the problems you used to have with WSDLs in SOAP services where Java wouldn't render the objects quite the way that .NET was expecting it, that wasn't quite the way that C++ was expecting the stuff to work.
Well, since it's all done through the same executable, it's all done in a way that's compatible. This means if you have a business service team that's writing services in Java and a web development team that wants Node.js services, and maybe you're writing a server in C sharp, and you have an Objective-C team that's writing a mobile app for a Windows phone,
or sorry, an iPhone, they're all able to actually have the code generated, and you know the things will work together. So next I want to talk a little bit about what is Cassandra. Well, Cassandra is a distributed NoSQL data store built for large volumes of data and fast read-write performance.
It is an open source Apache project. It's built with a no single point of failure architecture. And it's scalable. And by scalable, I mean it's built to be horizontally scalable on
commodity hardware. So if you're going to scale something like a relational database, usually the way you scale it is vertically. You get bigger servers with more processors, more memory, more disk space. With Cassandra, being as it's built to be horizontally scalable, what it means if you need to get a bigger cluster, you end up just adding more servers
to your cluster, and those servers tend to be cheaper because they're more commodity hardware. So a lot of people may be familiar with the CAP theorem. Well, what the CAP theorem states is there are three basic properties of data stores, consistency, availability,
and partition tolerance, and that you can choose two of them. Well, what consistency means is that all nodes will see the same data at the same time. Availability means that every request to the store will receive a response, and partition tolerance means that the system will continue to work even during network failures. Well, if you look
at something like Oracle or SQL server, they're both consistent and available. All the data is the same across any of the parts of it, and every request receives a response. In the case of something like Redis or MongoDB, all the data will be the same, and it's resilient to network failures. That's at the cost of not always
being able to answer every request. Well, Cassandra, it falls under what's known as an AP, or an available and partition tolerant data store. So what that means is every request will receive a response. It responds very well to network
failures, but what you lose is the ability for some of the nodes, sometimes the nodes will not all see the same data. So Cassandra is what's called an AP data store, and it has what's called eventual consistency. We'll talk a little bit about what eventual consistency means here in a moment. So another comparison that's often made between relational and the NoSQL world
is the concept of an ACID transaction. So if you're familiar with what ACID compliant databases are, basically an ACID compliant database has four guarantees. It guarantees that every transaction is atomic, meaning that they're all or nothing, and that they're consistent, that once data is, that as soon as a transaction is completed, all data is the same, that
transactions are isolated in as much as one transaction will not interfere with another transaction, and durable, that once the transaction is done, the results are permanent. Well, one of the problems with getting those four guarantees is it actually tends to have a drag on the performance of your data store because some of
those guarantees actually require quite a bit to actually achieve. Well, Cassandra is not an ACID compliant data store. In the NoSQL world, what you end up giving up sometimes is for some of those things in order to gain performance is you end up giving up a couple of those guarantees, and they've come up with this other corollary or comparison called a base
compliant database. What that means is it has a base availability where the data store is always working. It has a soft state, which means that stores are not write consistent, so data may differ between nodes or replicas of the database or data store, and that is eventually consistent. This means that stores become consistent over time.
So now I want to talk a little bit about what the Cassandra architecture is. So the Cassandra is architected as a cluster. It's a group of servers. You don't really ever run just a single Cassandra server. You actually run them as a large chunk of servers. And as part of that server, there's a hierarchy inside the cluster as to how
things are built and scaled out in order to provide that no single point of failure style architecture. So the smallest part is a node. Well, a node is a server. You know, node is a server. Next step up is a rack. A rack is very similar to, like, a physical rack in a data center.
It can be a physical rack or a logical rack, but basically it's a set of servers that will fail together if they fail usually, if the rack fails. Usually in case of, like, if you want to think about it as an actual data center, you know they have the same power strip, they have the same network. Well, racks then are grouped together into what's called a data center. A data center is made up, as I said,
of multiple racks, which is made up of multiple nodes. A data center is very much the same as a physical data center. It can be, yet again, be physical or logical. And in Cassandra, you actually have the ability to actually have multiple data centers. So you may have one data center that's located here in Norway and another data center
that's located in the United States. One of the unique features of Cassandra is the fact that data will be automatically replicated between the two. So you can actually write data here and read it out of the Americas. This means that if you have an international style problem that you're trying to solve, you're able to actually optimize your applications to write
to the, to read or write from the data center that's closest to them without having your application actually be aware. So the next thing I wanted to talk a bit about was the hash ring architecture of Cassandra. So as I mentioned, Cassandra is built out as a cluster.
Well, in that cluster, each node, there's this concept of what's known as a token ring. A token ring is a set of unique tokens between two to the, I think they run from two to the 63rd to two to the negative 63rd. I don't remember the number of unique values that exists,
but that is a number that I've never seen before. So for simplicity's sake here, I've just shown it as zero to 100. Well, the way that Cassandra works and stores data is each node owns a portion of this token ring. So in this case, we have four nodes.
You know, node zero owns one to 25. Node 25 owns 25 to 50. Node 50 owns 50 to 75. And node 75 owns 75 to 100. Well, each node owns a certain set of that token ring, and there's a piece of code inside Cassandra called the partitioner. Well, the partitioner
basically generates, uses a consistent hashing algorithm to generate a token from a piece of the data that you are reading or writing called the partition key. We'll talk a little bit more about that in a few minutes about what that actually is, but just know it's a piece of the data you're reading and writing from the data store. Well, it takes that partition key, it applies a hashing algorithm, and then it basically,
from that, it gets back a token. And that token is what determines which node owns that data. And one of the key features, as I mentioned, was the no single point of failure aspect of Cassandra. And because of that, everything is a peer. There's no master, there's no slave. So everything knows everything about all the nodes. So all the nodes know
which token ranges belong to which nodes. So here's a little example of how tokens actually were going to work. So you have a client, he's making a call into node 75. You'll often see node, the node that's being read or written to being called the coordinator node.
So the first thing that the coordinator node does is it takes the primary key out of the data that's being read. Well, in this case, the primary key is expiro. It sends that, sorry, not the primary key, the partition key. It sends the partition key to the partitioner, which applies that consistent hashing algorithm.
That consistent hashing algorithm will always send back the same data for the same partition key. In this case, the token that is returned is 12. So now that we know that this write is going to whatever node owns the partition key range that includes 12. Well, that partition key is owned by node 0, so that is how the data is
determined where the data is written to. So that after the data's been written, one of the other key architectural features of Cassandra is that data is replicated. You can specify the number of times data is replicated within a data center, and that is called the replication factor,
or what you'll see as RF. One important thing to note here is all of this is done without the application having to have any concept of how it actually works. So the number of replicas, you know, basically specifies the number of times a data is copied within a data center. If you have multiple data center setups, you can actually specify different copying,
different number of times it's copied between data centers. So here in Norway, you may say it's copied three times inside a data center. In the United States, it may be copied twice. That's one of the other key features is the ability to actually tune that based on your needs. And the last thing here is, yet again,
as part of the single point of failure, if now I'm replicating this data multiple times, what happens if one of my nodes is down for some reason? Well, there's this functionality inside Cassandra called a hinted handoff. What it means is the coordinator node, the node that you're basically writing or reading your data to, will basically know, okay, I need to write to node one.
Well, node one's down. It will store that data locally until such time as node one comes back online, and it'll then automatically sync that data back to it. So here's a little example of how data replication will actually work in real life. So in this case, we have a four-node cluster. We have a replication factor of three. That means the data's gonna be written
to three of these four nodes. Well, first thing that happens is a write comes in to the coordinator node. In this case, the write is A. As we talked about, the next step is the partition key is pulled out of the write, sent to the partitioner. The partitioner then sends back the token. In this case, 12. Well, the coordinator node knows
that the token with the number of 12 is owned by node one, and it's written to node one. At the same time, we know we need to replicate this data two more times. So while it's being written to node one, it's actually written at the same time to nodes two and nodes three. The way the cluster determines what nodes
it needs to write to is, if you have a replication factor of three and it's owned by node one, it just writes it to the next two nodes in this cluster, so nodes two or node three. If it was owned by node two, it would write it to node three and node four as the replica data.
So what does it mean to be eventually consistent? It means that data will eventually match on all the replicas. This isn't in terms of seconds or hours or days. It's actually in terms of milliseconds in almost every case. One of the other key parts of eventual consistency is what's known as the consistency level,
or what you often see written down as CL. There are 11 different levels you can choose for writes and 10 different levels you can choose for reads. But what consistency level means is it's the number of replicas that have to respond back that they've either read or written the data correctly in order for your request to succeed. So even though it's being written to three different replicas,
maybe you have your consistency level set to one, which just means any one of those has to respond back to me that it's written the data successfully for my application, my client application, to get back a successful ACK. Well, because of this ability
to be able to change how many replicas need to respond, you can actually tune the consistency level to affect the performance and availability of your application. Because, you know, let's take the example I just talked about where we had a consistency level of one.
Well, if you have a consistency level of one, that means the fastest one to be able to write the data is going to be able to respond back to me first if I have a consistency level. There's another consistency level called quorum, which is very commonly used, and basically it means a majority. It means a majority of your replicas have to respond successfully in order for that to happen. So in the case of a replication factor of three,
that means two of them. If you had a replication factor of five, it would be three. Seven would be four, and so on. But the more nodes that you end up requiring to have written the data in order for your request to succeed, the slower the performance of your application is going to be. Well, the other part of this is the availability. There's a consistency level that's known as all,
which means all of the replicas have to respond back, so it's very similar to a... Actually, in that case, it's more like an ACID transaction where everything has been written and all the data will be consistent. The problem with that is, if any of those nodes are down, now every request is going to fail. So if you choose that,
it's very rarely actually used in real life because of that downside, but if you choose that, know that any of your requests will fail if one of your nodes is down. And lastly, the consistency level can actually be tuned for read and write performance on a per-query basis. So different queries can be tuned
to have different consistency levels. Maybe you have one that you need all on because you really want to make sure that if it's written, it's written everywhere. In that case, you can actually use all, and maybe you have another one where you don't need as high a guarantee, so you use consistency level of one. The ones you most commonly see use are one and quorum.
Well, one quorum, and there's actually another concept in there called local one and local quorum. And what that means is, if you have multiple data centers, it's only waiting for the data in the local data center you're writing to. It's not going to wait for anything to go across in the case of having one in Europe and one in America. You're not going to wait for anything to go across the ocean in order to do that.
And this is all tuned by, basically, in Cassandra there is something called CQL, which stands for Cassandra Query Language, which is actually, we'll see it here in a little bit, but it's very SQL-like. So, why might you want to use, like, Cassandra over a relational database?
Well, the number one reason probably is performance. Cassandra is optimized for high, very fast read and write performance. And when I talk about very fast read and write performance, we're talking single digit millisecond read and write efforts. It's pretty much something you're never going to be able to actually achieve out of a relational model. It's also linearly scalable.
What do I mean by when I say linearly scalable? I mean if you have twice as many nodes in your cluster. So, if you add twice as many nodes to your cluster, you will be able to process twice as many transactions. That's not really true in any relational database. If you add twice as big a server, you're still not going to get twice the performance out of that database. It's also natively built as a distributed data store.
And what I mean by that is the fact that it's built to handle data that's too big to fit on a single server. So, if you have a relational database and you need to scale it up to data that's too big for a single server, what do you end up doing? You end up having to build clusters. You end up having to shard it. You end up having to do relatively complex exercises
if anybody's had to go in about this. And that has a lot of overhead involved in actually getting it to work. Well, since Cassandra is natively built as a distributed technology, it can handle pretty much any size scale of data. You know, terabytes, tens of terabytes, hundreds of terabytes, it doesn't matter.
And because of the distributed nature in which it's built, it's actually built for an always-on style architecture. So, you can actually have a cluster that is never not available. So, if you start doing a lot of research on Apache Cassandra, what you'll find is you'll find this concept of...
You'll see a lot of references to data stacks. And I just wanted to throw a little bit in there to tell you what the difference is, so you don't get confused like I did when I first started. Well, so, as I mentioned, Apache Cassandra is an open-source, free project. What data stacks is is data stacks provides a commercial version of that project
with additional features added. Some of those additional features are it has a full-text... It has integration with a full-text indexer based on Apache Solr. It also has integration with an analytics tool based on Apache Spark. So, those two things are tightly integrated. That's available, and data stacks has two editions.
There's community edition, which is free, and there is an enterprise edition. The enterprise edition also adds some additional features around enterprise-level security, integration with an active directory, has some additional tools around automating some of the management tasks you end up having to do with a Cassandra cluster. And there's a tool.
It also comes with it called opcenter, which is a very nice way to monitor the health and performance functionality of your cluster. And probably the last and most... The biggest reason I see a lot of people do it is it comes with a support ticket. You know, being an open-source product, if it breaks on you, you end up having to go to news groups. You end up having to, you know,
post things on Stack Overflow to get help. Well, if you have the enterprise version, you actually have someone you can call. So, next, I want to talk a little bit about a... You know, here's a problem where you may want to start thinking... You may want to actually use, you know, GRPC and Cassandra instead of a REST and relational database sort of model.
So, you work for a company that does a SaaS product that monitors truck engine sensors, basically. You know, you currently have, like, 1,000 trucks in your fleet, and you take readings every 10 seconds. This is built on a Web API, you know, REST set of services on top of a SQL server database.
I'm guessing we've all built something probably very similar to this before. Well, you recently just landed a huge new client. They love the UI. They love how the product works. They just want some changes to how the data is actually read out of you, how the data is captured. Instead of reading it once every 10 seconds, they now want to read it once every second.
They also want to add latitude and longitude information from their truck's GPSes so they can see not only where it failed, or not only when it failed, but where it was at at the time. They're gonna add 10,000 trucks to your system, and part of their contract was you needed to minimize their costs and provide them a zero-downtime environment.
Well, what's the problem? Well, now you've gone from a transaction load of about 100 measurements a second to 22,000 measurements a second. That's a relatively large increase. And, you know, your data load's gone from about 35 megabytes a day to 2.2 gigabytes. Well, your architecture's gonna need to change
in some way to handle this additional increase in scale. I think we can all agree. So here's the solution I'm proposing, which, just in case you are interested, all the code is available on GitHub if you want to take a look at what we did here. Well, the solution I'm proposing is, first off, we're gonna change out the SQL Server database for a Cassandra cluster.
This will provide some of the scalability of not only the transactional load that's gonna be required, but also the data load that's gonna be required. Because at 2.2 gigabytes a day, how quickly is that gonna take to fill up a single server? Won't take that long. And we're gonna replace our REST-based set of services with a gRPC-based set of services.
So the first step here is to define the model and service. We've seen this a little bit already with... earlier on, where this is basically using protocol buffers
to define out the models and services and service endpoints that you're gonna have. So in this case, you have a service endpoint called trucking. It's got two different... You have a service called trucking. It has two different endpoints. One of them is called recordLocation, and the other one is called readLastLocation.
So if you look at readLastLocation, what it's gonna do is it's gonna take in a trip object, and it's gonna return back a point object. So it will read the last location that's been stored in the system. And then you have one called recordLocation. And this is where it kind of gets a little bit interesting from an API standpoint. This is one that actually will take a stream of points
and return back a stream of responses. So this is an example of how you can set up that bidirectional streaming functionality that I talked about that gRPC had. So you're able to set that up that way. You'll notice then there's a few objects of a point. One of the things to notice about the point
is the first property is actually another object. So with protocol buffers, you are able to build out complex domain objects and transmit them back and forth across the wire. So now that we've defined our client and server stubs... Sorry, the text is actually pretty wonky here.
What you'll see at the top there, if you could read it, is that basically this is the command-line tool code that you'll need to run to actually generate out the client and server stubs. What you can see is, basically, it's a call to that protoc file, and then it's a bunch of command-line arguments to specify where the proto file is
that you want to actually compile and where you want the outputs to go and what you want those outputs to be. Unfortunately, I know most of us all here are Windows guys and don't really like command-line stuff, but at this point, there is no GUI toolkit to do this. But like I said, it's still a relatively new technology,
so I expect that will probably come along at some point here in the future. So you run that command, that big, long command, and what do you get out of it? You end up getting out two separate CS files. One of those files will be called truckingservice.cs, and that will contain all your model definitions. The other one is called truckingservice-grpc,
and that will contain the GRPC client and the client stub and the server interface that you will need to actually implement for your service. So, now that we've created the service, now that we've defined the service and we've created the stubs, the next step is to go and actually create
the key space in the table. So in Cassandra, you have what's called a key space. A key space is basically the same as a database is in SQL Server. It's a collection of tables. What you can see here is that the, sorry,
what you see here is actually, this is all CQL that you're seeing on screen here. As you'll notice, it's very similar to the way SQL looks, and it's actually very similar to the way SQL functions. So, first thing you'll see here is that we're gonna create a key space called NDC Oslo, which is, which with that we'll just do
is create a new key space called NDC Oslo, and then we're gonna set the replication on it. This is where you are able to set the replication factor of your system. We talked about the replication factor earlier is the number of nodes that data is copied to. Well, there's a couple of properties you can set, the first being the class. There's two separate classes you can set. If you're working on a single data center,
you'd use what's called simple strategy, and then you can set the replication factor. In this case, I have it set to one because I was running this on a single node cluster, which is not much of a cluster, but... The other one is, if you're working in a multi data center environment, you'll get, there's one called network topology strategy.
The interesting thing about network topology strategy is that you're able to set the replication factor on a per data center basis. So you can set up multiple data centers. They can be of different sizes, and you'll still actually be able to set different replication factors on them. So, once you run that command, it'll basically create your key space. The next thing we're gonna do is run a use command,
which will just put you in the context of NDC Oslo. This is exactly the same as doing a use command in SQL. So now that we're in the context of the key space we've created, we're going to create a new table. This should look very familiar pretty much to anybody that's done a lot of SQL. I'm just gonna create a table called location by trip ID.
I'm gonna set a few properties. You know, there's a truck name, a manufacturer, a trip ID, a time, a latitude, and a longitude. I guess one key thing to note is if you're actually using Cassandra, the timestamp is all in Unix time epic, which can be kind of interesting to get to from a dot net world if you have never had that fun.
So the last part you'll see there is actually the primary key. So the primary key is not the same as a primary key in a relational database. In a relational database, the primary key is used as an index, and it's used to actually provide relational data integrity across tables. In Cassandra, what it's actually used for is it's made up of two separate parts.
The two parts are the partition key and the clustering keys. What you'll see in this example is we have truck name, manufacturer, and trip ID, and those are what's inside a set of parentheses, and that's what makes the partition key. If you remember from earlier, the partition key is the piece of data that is taken and given to the partitioner
in order to make the token, and the token determines which node owns that data. So that's what the partition key does. The key after that is the clustering keys. Right now, I only have one clustering key of time. You can have many clustering keys, and then you can specify the order
in which you actually want them to be written out, ascending or descending order. Well, the point of the clustering keys is, so once you know that the data is going to be written to node one, for example, what actually happens on the actual Cassandra node, when the data is written, it's written, each partition is written to a separate file. Well, once it's written to a separate file, the clustering keys tell you
what order to write the data into the filing. In this case, the data will physically be written into the file in time-descending order. So now that we've created our database, or created our key space, we've created our table, we've created our stubs, and we've created our objects,
the next step is to actually attach to the cluster and start doing some work. Well, there's an open-source C-Sharp driver that's provided by data stacks at the address you'll see there. And basically, if you want to connect to the cluster, you basically build it out as you see here. You know, you're basically a cluster.builder, and then you add contact points.
Well, in this case, I'm showing you want to add... I'm showing adding multiple contact points. Well, why would you want to add multiple contact points? Well, the reason you want to add multiple contact points is, one, it provides the ability for no single point of failure, because, say, in this case, node 2 was down. If that was the only contact point listed there, your application would not be able to actually read or write data from the thing.
The second point is, you want to be able to spread the load of those read requests and those write requests across the different nodes in your cluster. Since every node in the cluster is a peer, there is no master. Any node can handle a read and write request. So if you put all the nodes in your driver, what will actually happen behind the scenes
is the driver will automatically round robin across the different nodes to spread the load out. That way one node is not taking the majority of the read and write requests, so it helps prevent overloading a single node in your cluster and slowing down the performance of the cluster as a whole.
So now that we've connected to the cluster, the first thing we want to do is write some data to it. Well, if you're familiar with SQL, as I'm guessing probably everybody here is, this should look very familiar to you.
You're just gonna insert data into the table name, then you're gonna specify the columns that you want to insert, and then you're gonna specify the values that you want to insert into that column. I mean, you could take this and pull it right into a SQL database, and it would work just fine. One interesting fact to note about Cassandra is when you're doing inserts or updates in Cassandra,
it does what's called an upsert. This is actually a not uncommon thing in many NoSQL databases, and what an upsert is, is if you're familiar with a SQL merge statement, it's the same functionality. What it means is if the data exists, it will update that data. If the data does not exist, it will insert that data.
So now that we've successfully written data to the data stores, let's read some data back from it. Well, how do I go about reading data back from it? Well, you again write a CQL command, which is very similar to the SQL command.
You know, I'm gonna select star from my table name, where some filtering criteria, in this case, the truck name, the manufacturer, and the trip ID. And then there's this idea called limit one. What I'm doing here is I'm actually limited to only the top result, so I will actually only get back the last read,
the last location ever written by time. That's where the clustering keys come in. It's very similar to the concept of a top one in SQL. So a couple interesting things to note here is there are some constraints around how you can actually filter data in CQL. In CQL, you can do a select star from a table,
in which case it will go out, it will scan every cluster in the table, or every node in the cluster for that table information and try to correlate it together. If you try to do that on a cluster of any size, it'll probably time out. It doesn't usually take too long before you get to the level of it just times out
before it actually is able to process all that data. So if you want to add a filtering criteria to it, there are some constraints. Some of those constraints are, the first one is, if you're going to add a filtering criteria, you have to add, at minimum, every key in the partition key as an equality clause.
This means, because my partition key consisted of truck name, manufacturer, and trip ID, I have to have truck name, manufacturer, and trip ID as equality clauses in my filtering, in my where clause, in my filtering clause. That's because, as we said, those are the three items that are used to determine,
used by the partitioner to determine the token. So what, the way that work, the way it works is it takes us, when you do a select query, it takes those three keys, it finds out what the token is, and then it's able to very quickly go straight to the node that owns that data, to the file that owns that data, and pull that data back out.
That's one of the ways in which the performance of Cassandra is enhanced by adding that constraint on. The second constraint is, if you want to actually filter, or if you want to filter on anything beyond the partition key, these must be one of the clustering keys. So you have to have, so basically, in this case, the only additional item,
column that I could actually search on is time, because it was the only clustering key I added into it. If you're searching on that, those can be inequality searches, if they're clustering keys, but they have to be equality keys, equality searches if they are partition keys.
So, next I want to actually show you a little bit of running code. So what you'll see here, this is actually the client side of the application. What we were showing before was everything that's written on the server side. So this is the client side. So, in order to create a client, basically the first thing I have to do
is I have to set up a channel. All a channel is, it specifies essentially where is the endpoint I'm going to be talking to. Since this is over, you know, since it's over HTTP, in this case, I'm actually gonna run it against local host on port 50051. First thing I do is come in here,
and I start, I basically start a new asynchronous task called start truck recording. Well, let's go look at start truck recording. Well, start truck recording, first thing it does is it creates a new trucking client. That is the client stub that was generated by the, generated from the definition.
So, between these two things, I have now been able to connect, those two lines of code will basically now connect your client to your server. Much simpler than a rest style architecture. Well, first thing, next thing I'm gonna do is I'm gonna basically set up a call to record location.
Record location, if we remember, was that bidirectional streaming endpoint. So, we can actually, this is an example of how you actually will write a bidirectional streaming to be able to stream data back and forth. You know, the theory in this case is a truck, you know, when a truck starts up, it connects once, and then it's able to stream data back and forth to get, and what it's actually doing, it'll stream,
it'll stream latitude and longitude data, and it will send back the actual time it took to write the data out. That becomes important because the first step here is we're gonna set up an asynchronous task to basically wait for those response messages to come in. So, those response messages come in.
The way I have this written is it'll write, every fifth, it'll basically, it writes every fifth response message that comes in, it will take the average of that data, it will then call a read last location, so it will actually read data out of the data source, and then it will print all that information with those times out.
So, after you set up the task to read the responses back, the next step is to actually basically just start sending data in. So, this code, all it does is create a new point with some made-up data, and write that asynchronously once a second to the server.
So, what you'll see here is it's now, the servers are connecting together. That first one is because the server had not started out by the time it tried to connect. So, you'll see it's writing out data, and it's taking about 178, or 170 or so milliseconds to read and write data to and from the cluster.
Well, that doesn't seem super performant to me. I don't know about you guys, but 170 milliseconds is pretty slow. However, that's because the cluster this is currently writing to is located in Oregon in the United States. It's about 7,700 kilometers away
and has about a round-trip ping time of about 164 milliseconds. So, if you figure 164 milliseconds out of, you track that from the 170 milliseconds we were seeing, it means it was taking about five milliseconds to write that data out, which seems a lot better to me. If you can see on the left, or on the right there,
I actually ran this locally in a data center in Oregon to get more realistic numbers, and the numbers there were anywhere from about one to five milliseconds to actually write. I think that's probably a little bit misleading. I think at that point you're down into the accuracy of the .NET stopwatch class because I don't think it was actually reading and writing
within a millisecond. It seems a little too fast from my experience. But I have seen many times in my experience I have seen where that data will be read and written in about somewhere between 3 to 10 milliseconds. So, now I just wanted to talk a little bit
about some of the trade-offs when using gRPC in Cassandra. So, some of the trade-offs when using gRPC. First off, and probably the biggest trade-off, at least from my perspective when using gRPC, is that it's not for browsers at this point. Unfortunately, it relies heavily
on HTTP2, HTTP trailers functionality. And so far, no modern browsers have had a robust enough implementation of it in order for them to be able to build a client against it. There is hope that this will be coming in the future. I really hope to see this in the future because it would make it so much more useful.
The other part is, if you have large messages that you're passing back and forth, anything larger than about a megabyte, you end up having to chunk that data. Theoretically, you can write gRPC messages that are up to about, I think, 63 megabytes, but the practical limit in what they suggest you use
is no longer than one megabyte. This means if you have a five-megabyte file, you have to chunk that data up. You have to send it back and forth. And unfortunately, there's no built-in functionality currently in gRPC to handle the chunking, so you end up having to write it yourself. Also, in protocol buffers version 3,
which is what all of this runs on, which is what all gRPC runs on, there's no support for nullable data types. I find this relatively annoying because if you end up having a value that's an integer and it comes across as a zero, does that mean it's a zero, or does that mean it's a null? You end up having to do some work.
It's optional, but when it's generate, yes. Yeah, yeah.
Yes, yes. Yeah. And probably one of the other things, especially if you want to use this in production, is it's still a beta release. So it's a beta release, so a lot of places, especially a lot of enterprises, won't allow you to use it in production yet. It's a strong beta. I think it's on beta 14 at this point.
So it's been around for a while. So what are some of the trade-offs when using Cassandra? Well, probably the biggest trade-off when using Cassandra and the biggest limitation that you'll actually run into is the fact that you are not allowed to join between tables in Cassandra. So what that means is, it means exactly what I said as far as
if you want to get data out of a table in Cassandra, all that data must live in one table. This leads to a lot of denormalization and data duplication. Basically, when you do data modeling in Cassandra, it is far more important than it ever was in a relational model, because you have to build your...
Basically, your Cassandra keyspace is built on what's called a table-per-query methodology. So if you're going to build out your keyspace, the first thing you have to do is figure out how you're going to get the data out of your application, because if you get the data out of your application, you're not going to be able to actually build out your tables in a way that you can be able to fetch that data,
because one of the second limitations, as I already alluded to, is that you can't really do ad hoc queries. If you want to do queries that have filtering, all those filtering criteria have to live, have to be part of the primary key. That's not necessarily completely true. There are several other methodologies. There's this concept of secondary indexes
and materialized views, and actually, if you're using the data stacks, you can use the full-text indexer and query against it. However, each of those has an effect on the performance, and generally, and in general, they're discouraged from being used unless there's no other way to do it. Cassandra also has minimal support for aggregations.
It has support for some, min, max, and average, but those are the only aggregators it supports, and in general, because of the performance hit that it requires to actually make those work, they're not recommended to be used. As you probably now have guessed, the complexity of Cassandra is far more than that
of a normal relational database. Part of that's being just the nature of being a distributed data store. Part of that is just the complexity with which it's required to get the performance that people are looking for. Cassandra is not something you're gonna sit down in a weekend or an evening
and figure out how to use correctly. It's gonna take some time in order to be able to understand what the limitations are, what the advantages are, and how you might best use it in your scenario. And the last one here is Cassandra's not relational. The biggest mistake I see with a lot of developers that are coming in to try and use Cassandra is they just try to use it like it's a relational database.
Well, if you try to do that, you're pretty much guaranteed to fail. You need to spend some time. You need to understand it. You need to understand its limitations and then figure out how you're gonna apply it. You can't just throw your relational database schema in and expect to actually be able to accomplish anything from it. So now if you're interested in learning some more,
there's a couple of links I have up here. Specifically, if you're interested in learning more about Cassandra, I would highly recommend you go to the academy.datastacks.com link. They actually provide a lot of very good, free online training. You do have to register for it, but all the training is free. They have several very in-depth courses
on not only Cassandra, but the other aspects of the DataStax system, including Solr, Spark, and actually, they recently came out with one. They're going to be... DataStax is going to be releasing a graph engine on top of their version of Cassandra, and they recently released a graph training on that as well.
So with that, I would like to thank NDC Oslo for inviting me to come speak, and I'd like to thank all of you guys for coming and listening to me. Are there any questions? How do you do security between the nodes when replicated?
You can actually set up SSL to secure the data between it, and you can also secure the data and write state as it's being written to disk. Yes. No, there's not.
Unfortunately, that's one of the limitations you have is that partition key has to be equality comparisons, and the reason for that is because those equality comparisons,
it needs to be equality comparisons so you can get the token out so it knows exactly where in the cluster to get it. That's one of the trade-offs you end up making in order to get that very fast read performance.
Absolutely. Yeah, if you have nodes on a network that's very slow, yeah, it will dramatically affect the performance overall in general, and then the consistency level is very... Yeah, the consistency level you can use to probably tune that to make it slightly better.
You know, you may want to make it 1 instead of quorum, but, yeah, you pretty much have to have a very fast network between all your nodes internally in order to make it very performant.
What happens to that data? So let's say, for example, you have... The node is owned by node data... The data is owned by node 1, and node 1 is down. Well, that's where those two other replicas come into effect. If node 1's down and it queries data that's owned by node 1,
then the replicas from node 2 and node 3 will service that request. Because we have a replication factor of 3, yeah. You know, really, what owning it means is it's the first of the nodes
to have that data. So Cassandra, the actual distributed model is inspired by DynamoDB, so that aspect of it is different. So Cassandra, it was originally built at Facebook.
It was inspired by... The distributed design was inspired by DynamoDB. The data model was inspired by Google Bigtable. As far as recommending it, you need to know more about the problem before I could say one versus the other, but they're very similar in a lot of ways.
As far as I know, there is no one that provides a hosted service. However, you can spin up AMIs in AWS, and actually, Windows Azure has built-in support now for,
as of, I think it was like November, they signed an agreement with Microsoft to have built-in support for Cassandra. Oh, sorry. Actually, it's not Cassandra. It's data stack specific. Sorry.
A classic geotime series database? Probably the biggest would be the scalability aspect of it. You know, if you start putting in, you know, 2.2 gigabytes a day, it's going to not take that long in order to overwhelm many databases.
So... Okay.
Well, are you talking gRPC, or are you talking the connecting to the cluster? Because the round robining in the driver was done in the Cassandra cluster, whereas gRPC, you specify the specific address that you're connecting to. No. GRPC, it's run just like any other web service.
There's one endpoint. The round robining I think you're talking about was inside the... Oh, yes, that is not... This isn't gRPC. This is actually connecting to the Cassandra cluster. This is the Cassandra driver.
Yeah. So it is actually a binary that runs its own server inside of it. If you want to... The way I've always done it when I've done it in Windows is just built a Windows service around it to run it.
What I showed here is actually just a console app. If you want, I can show you how it actually... Okay. There is a few base packages you have to add
around... There's, like, gRPC core, gRPC.native.c sharp. They're all new gettable packages. And then what's generated from the model definition is actually an interface that you have to implement.
No, you probably wouldn't... In this case, you probably would actually have to build a couple of gRPC endpoints, and then you could put whatever load balancing in front of it that you wanted to.
All right. Well, if there's no more questions, I thank you. And if there's any other questions, I'll be around here, if anybody has anything else. Thanks.