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

Caching: Go big or go home

00:00

Formal Metadata

Title
Caching: Go big or go home
Alternative Title
Go big or go home, scaling through caching
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
Could you survive Black Friday traffic loads, or will your app fall over if it goes viral. Do you know what your etags are doing right now? All of these are important questions and this sessions aims to answer at least some of them. We will go through how to get caching working for you and show you some real world examples. It will start small at conditional HTTP requests and build up to application caching using a layered distributed cache service driven by pub/sub. "There are only two hard things in Computer Science: cache invalidation and naming things. " - a famous saying. Let's make sure we're only left with one hard thing, naming the next .NET.
Cache (computing)Function (mathematics)CuboidElectronic visual displayQuicksortDomain namePoint cloudProduct (business)Operator (mathematics)Connectivity (graph theory)Digital electronicsWebsiteMereologyCache (computing)DatabaseFunction (mathematics)Service (economics)Server (computing)2 (number)Web browserPlastikkarteExtreme programmingStructural loadCore dumpProxy serverRegulator geneWeb 2.0Scaling (geometry)Cartesian coordinate systemNumberPattern languageQueue (abstract data type)Medical imagingEndliche ModelltheorieWeb pageForm (programming)Streaming mediaInformationField (computer science)Musical ensembleTelecommunicationWater vaporVideoconferencingClient (computing)Heegaard splittingHill differential equationCrash (computing)Set (mathematics)Instance (computer science)Analytic continuationState of matterSemiconductor memoryXML
Cache (computing)Dependent and independent variablesConfiguration spaceLocal ringDefault (computer science)HeuristicLink (knot theory)Application service providerSynchronizationLimit (category theory)Latent heatDependent and independent variablesGoodness of fitLocal ringSemiconductor memoryImplementationCache (computing)Server (computing)Multiplication signKey (cryptography)Hash functionWeb browserFlow separationContent (media)HeuristicRange (statistics)EmailMereologyValidity (statistics)Application service providerQuery languageCore dumpLink (knot theory)WeightClient (computing)FlowchartType theoryoutputSpeicherbereinigungSoftware frameworkMaxima and minimaHeegaard splittingTerm (mathematics)NamespaceAreaMultilaterationSystem callMathematicsBuildingMatching (graph theory)Physical systemService (economics)Data storage deviceGroup actionMusical ensembleReading (process)Order (biology)Semantics (computer science)Level (video gaming)Extension (kinesiology)Computer animation
Extension (kinesiology)SQL ServerCache (computing)Physical systemInterface (computing)SynchronizationExecution unitOvalDefault (computer science)Maxima and minimaData structureServer (computing)Point cloudGoogolService (economics)Local ringMultiplication signAbsolute valueDefault (computer science)State of matterKey (cryptography)Semiconductor memoryRemote procedure callSequelImplementationPoint cloudDependent and independent variablesSystem callDifferent (Kate Ryan album)CASE <Informatik>BitMathematicsCommunications protocolMereologyGoogolCuboidWeb pageMessage passingInstance (computer science)Repository (publishing)Server (computing)Cycle (graph theory)Pattern languageProcess (computing)ArmClient (computing)MultiplicationInterface (computing)Metropolitan area networkObject (grammar)Physical systemProgram slicingEvent horizonRun time (program lifecycle phase)Structural loadData centerReading (process)2 (number)Magnetic-core memoryDemo (music)WebsiteWordData managementBenchmarkMusical ensembleQuicksortResponse time (technology)BefehlsprozessorAmerican Vacuum SocietyBand matrixCache (computing)CodeLibrary (computing)Configuration spaceSerial portLastteilungWeightBuffer overflowStack (abstract data type)Online helpSoftware bugBinary codeSource codeBinary fileComputer animation
Default (computer science)Twin primeSynchronizationExecution unitDatabaseSerial portString (computer science)OvalCache (computing)Physical systemStreaming mediaIcosahedronData typeInterface (computing)Color managementEvent horizonWechselseitige InformationReading (process)Term (mathematics)Library (computing)Interface (computing)System callConnected spaceServer (computing)Instance (computer science)SoftwarePlanningInsertion lossType theoryProduct (business)Message passingMultiplicationVirtual machineResultantChemical equationGreatest elementRemote procedure callBand matrixForm (programming)CASE <Informatik>Physical systemRule of inferenceSemiconductor memoryKey (cryptography)Uniform boundedness principleData storage deviceLocal ringProcess (computing)Different (Kate Ryan album)Repository (publishing)Binary codeLine (geometry)Real numberImplementationCasting (performing arts)Database normalizationBitCache (computing)Pattern languageRevision controlSinc functionStreaming mediaCodeSingle-precision floating-point formatSerial portEvent horizonSynchronizationGoodness of fitClient (computing)Cartesian coordinate systemNetwork socketWeb 2.0Computer animation
SynchronizationOvalCache (computing)Type theoryKey (cryptography)Local ringComputer programCoprocessorInformationMilitary operationException handlingElectronic mailing listScripting languageServer (computing)Rollback (data management)Hash functionTerm (mathematics)Reading (process)Maxima and minimaElement (mathematics)Key (cryptography)Instance (computer science)Computer programmingProcess (computing)QuicksortLocal ringMultiplication signScripting languageSemiconductor memoryWordBlogService (economics)Message passingPoint (geometry)Server (computing)2 (number)Reading (process)Element (mathematics)Data storage deviceSet (mathematics)Response time (technology)Hash functionSequenceDrop (liquid)Object (grammar)DatabaseElectronic mailing listOperator (mathematics)Uniform boundedness principleGoodness of fitDatabase transactionCASE <Informatik>Uniqueness quantificationMereologyStreaming mediaRight angleVirtual machineNumberPhysical systemBuildingNormal (geometry)Universe (mathematics)MathematicsTrailBand matrixMetadataData modelSoftwareCartesian coordinate systemCache (computing)Remote procedure callElectronic visual displayAbstractionData typeBit rateBuffer solutionComputer animation
Scripting languageObject (grammar)Archaeological field surveySemiconductor memoryCache (computing)JSON
Transcript: English(auto-generated)
Thank you all for coming. Just one practical thing, there's a box outside, somebody wants you to push one of those buttons. My name is Harald Schütter-Dixon. I work at a small company called Audum.
I'm currently in parental leave, but when I'm not, I've been with NRK on the TV and radio player team for almost two years. A lot about this caching is from our experiences and how we solve things there.
So let's dig in. Black Friday. That is not something new. It has become the manifestation of how we're not able to scale. It happens every year. We all know Black Friday is coming. And sure enough, some sites crash.
On the left side, we have Best Buy. Failed miserably, unable to serve their customers. On the right side, we have Walmart. They've been quite cocky for some time. After they switched to a new platform, they've not had any problems. They did a deploy to production during Black Friday.
That's how confident they are. We have the same situation in Norway. And we're a lot smaller country. But if you tried to go on the midnight of Friday to any of the electronics stores online and shop, it failed.
Komplet was serving 200 OKs. They did render a page within a minute. That's better than the rest. The rest did not serve anything. And that's quite funny to see that you have all these shops, they've spent millions in advertising just not to sell you stuff.
That's a success. So we need to fix that. We need to talk about this, why this doesn't work. And then we see this is private sector. Everybody thinks that should work. And then we have government. We have the Norwegian edition of the IRS.
I guess everybody runs to check their taxes when you get the pre-filled form to see how much you get back or how much you have to pay. That fails as well. But I can understand that a lot better.
They are under heavy regulations. They cannot scale the same way in cloud. It's sort of fair. I can see why they can't do it. But they're getting better at it. And at NRK, our Black Friday is the election. We get a lot of traffic on the news site. The news site displays video.
They need metadata that comes from my team and we saw that this was not going really good. And one of the operations people went in and doubled the number of servers in Azure. Everything was running fine again. And during my rant on this, I said to a friend, you can buy 100 servers in Azure for 24 hours.
A3 instances, that's 7 GB of memory, 4 cores. That will set you back $860. And he said, you can't do that. You will take down your database. I don't want to hit my database. I see no reason why we should hit the database when you hit the first page.
And that's where caching comes in. There's a lot more to delivering an experience under extreme load than caching. It's just a small part. We have everything from domain modeling.
You have infrastructure. You can use queues. You have patterns such as circuit breaker. Especially useful when you depend on other services which they also get load during Black Friday. So perhaps your credit card service can't respond.
And then you need to sort of, can we take that component out? Can we wrap it in something like a circuit breaker and continue? And such things will help you a lot. I know the Komplet guys will speak tomorrow. I hope they will talk about this.
But caching is one part of making this whole thing work. And today I want to talk about two parts of caching. If you see from web client, we have HTTP cache that lives with the client. It can be a proxy on the way to the server but normally it's very well implemented in the browser.
We then have output caching, cached in IIS or browser-worn-ish server etc. And then at the other end you have the application cache. And you often need to hit your application because you have a user who is logged in.
You may need to display information for this user. So we cannot necessarily always use the output cache and just get rid of the problem. So today I'm going to talk about HTTP cache and the application cache. CDN is sort of, that's CDN so I'm not going to mention that.
Of course you have your static files, we have our streams and images will be served from CDN. So HTTP caching is defined in RFC 7234. It's part of the HTTP-based split where they split up HTTP specification into separate documents.
And the purpose of HTTP caching is to significantly improve the speed and experience for the user. This is done, there are two things that happen. One is that the latency goes down, the request is served locally.
Network traffic goes down. So that's a really good thing about HTTP caching, we don't hit our servers. But it's not always how it works.
And it's all about storing responses when you talk to the server. And hopefully reuse that response instead of calling the server. And it says that a stored response can be considered fresh if the response can be reused without validation. And this is somewhat like milk. You know that the content will expire at some time.
And you really don't want to use that milk until you check that it's okay. And if it's fresh, that is the, if a response is considered fresh, the client will just use it. It will not talk to the server. If it has to validate that response, it will call the server and it will hopefully get what is called a 304 response without any content.
And one thing to keep in mind when we talk about this specification is that it is not written as to how you should cache.
It is written in terms of what you should not cache, what should not be cached in the client. And it's also what should not be reused due to something. So I read and you can read this one.
So as I said, it is what not to cache and what not to reuse.
And the first part of it is determining is this response fresh. And that's all very well implemented in the browsers. I have an extremely good understanding of this. And you will have a completely different situation if you're making an API.
And you have API clients that are not browsers that are, let's say, the HTTP client in .NET. That will not have caching built in. So if you're trying to use some of these techniques within API, you absolutely need to get your clients to really use it.
You need to educate your clients. So these first three are headers. They are sent in the response from the servers. And it tells the client how long this response will be valid. The last one, heuristics, is when you don't have the other headers.
If the server does not give you a shared max age expires, the user agent will try to calculate if the response is still fresh.
And that is done by looking at the last modified you have in the local cache in the browser. It will look at when you last requested this. And then it will look at now. And if you're requesting it now, and the time between now and your last request is
10% or less of when the document was last modified, it will still be considered fresh. It will not necessarily call the server. This is only the suggested implementation. There's no specifics in the specification that says this is how heuristics works. But there's a suggestion on it.
So if it's not fresh, we can then ask the server, is this response still OK? And to do that, we utilize two headers.
There's ETags. Has anybody used ETags? A few, yeah. And there's a last modified. And these are combined with two request headers, if-not-matched and if-modified-since. So what happens is that the server will, or can, add an ETag.
Let's think of it as a hash of the content in the response. When the client wants to reuse the response with an ETag, it will send the ETag to the server with the if-not-matched.
So the server will see, OK, has this changed? If the server still has the same ETag, it will just give a 304 to the client. The client will get a really small response. There can be multiple ETags, so there can be multiple valid responses in the cache.
So the server may have one, and the client can have several. And if one of them matches, it will get a 304. ETags also comes in two flavors. There's something called a weak and a strong. The weak ETag means that it's not byte-correct.
So we cannot use that for range queries, but it's semantically the same as you have. A strong ETag means that it's byte-correct, so you can actually do range queries on it and just get a part of the document. So that's one way to do validation.
And then you have the last modified, which is a date. Now HTTP date is without milliseconds, so that means that if you have a frequently changing system, you will not be able to use the last modified for this, because you won't know if you have the same or not.
And you use last modified together with the if-modified-since request header to check if the server has changed since then. Validation can also be used for post inputs. But then it's used to validate if the server has changed when you post or put something, not for caching.
There's a few more links on this, if you want to read and dig into it. Mark Nottingham has written quite a few of the specs. He also has a tutorial on HTTP cache and how that should work.
Daryl Miller has created flow charts. If you really want to dig into how browser caching works, it's a really nice resource. And then there's a little link on heuristics. We're going to touch slightly on ETags later on.
So now that we have sorted out the client cache, let's move over to the server. With the ASP.NET Core, or whatever it's called now, currently the namespace for this is Microsoft Extensions Caching.
They are now delivering two types of cache with a new .NET Core framework. You have an in-memory cache. Which is powered by a dictionary.
It supports callback on eviction and when keys expire from a cache. You can put priorities on cache items. And it will compact the cache by 10% on a garbage collection. Whenever a garbage collection of Gen 2 happens, 10% of your keys in the cache will be thrown out.
And as they think that any garbage collection of Gen 2, that means that you have used so much memory, so .NET probably needs something to be freed.
And remember that if you are on .NET 4.5, there's a limit on 2GB in memory. Now this is Core, so I guess it's changed when we get to that. They also have a distributed cache. Useful when you start having multiple servers and you need a central cache.
It comes with three implementations in .NET Core. A memory cache based one. One which is based on Redis and one which is based on SQL Server. Now with Redis on Azure, AVS and Google Cloud, I assume most people will use the Redis implementation of it.
It's also worth noting that you will be responsible for serializing your objects in the cache if you want to use this one. Local cache, as simple as a dictionary. The data lives in memory.
So it's fast, it's a lot faster than going to another process, because the process is yours, it's available. A word of caution with local cache. These are objects, they are mutable. So if you retrieve something from the cache and change it,
and then you retrieve it from a cache on another request, it's changed. So we had a fun bug with that one. Or fun of fun, I think. We spent three days digging it.
So let's see how that can look. So what we have here is an interface following a pattern called cache-aside. Cache-aside is one of the patterns recommended by the Microsoft Patterns and Practices team.
For those of you who were on the Patterns and Practices event, Microsoft had it in Oslo, you should know this. So I'll just split the interface in two so we can concentrate on one part of it.
We have the get call to the cache, which takes the cache key. It takes a func, and this is where you call the repository, where you retrieve the data from a slow source. And then you have a time to live, which you can tell the cache,
I want this to live in the cache for that long. If we look at the implementation of this, it's fairly simple. The code I have here is on GitHub.
It's all running on the old .NET. So I use the system runtime caching memory cache. It calls and tries to get the key. If it finds the item, it returns it. If not, it will execute the func we passed in,
add the item to the cache, and return it. Quite simple. Here I have a small service.
There is a service out there called random-user.me. So if you need some JSON data, it's a nice place to get test data. So you see when I call this, it takes almost a second on this Wi-Fi. And then we can try it with the local memory cache.
We see that I'm 32 milliseconds. So it's working quite fine. We can... Let's just take that one. And we can let it run for some time. And see what we get.
It's not very interesting. Right now we know it will be fast, but it will be more interesting when we get to the remote caches. So, 4,600 requests per second. Really fast. Uses a lot of bandwidth.
So what happens when we move away from a local cache to a remote cache? So let's say you start load balancing your servers. Let's say you run in multiple data centers, etc. Is it okay that you have local cache on each server?
What happens if you hit server 1 and then after a while hit server 2? The cache is different because they've been initialized at different times. The user will get different responses. Is that okay? Probably not. And that's why you want to move to a central cache.
Such as Redis. So we did that at NRK. Actually we had the Azure Managed Cache. Has anyone used that? Yeah? One? Two? It was, I think, Microsoft's third attempt at making a cache in Azure.
What was nice about it was that it came with a client from Microsoft which had a local cache as well. So when you call the remote cache, you got something. You would automatically get it in a local cache. So we had the fast response times. Now when we move to Redis,
because the managed cache will be discontinued in December, so everybody has to move, Microsoft has no longer made a client for the cache. They use the client library from the great guys at Stack Exchange to talk to Redis. But that does not come with a local cache.
And the helpful MSDN page says that you should develop something yourself if you need that. And also I did. So what happened was that we saw the CPU went haywire. We got really terrible response times and we really tried to dig into it.
We saw that we spent almost 56% of the CPU time on de-serialization. And that's because we follow the guidance from Microsoft and use the binary format too.
All serializers are not equal. And Yan Cui, he's also a speaker here at NDC. He has done some benchmarking on serializers. And there's not only for binary serializers, there's difference. There's a lot of difference when it comes to JSON serializers as well.
His website is Burning Monk. So if you Google Burning Monk and benchmark, you will get to this page. And Bond there is from Microsoft. It will not be that fast unless you tweak it.
It comes with quite a bit of configuration which needs to be done to get it up to that speed. It has to do with object sizes, depth, etc. By default, Protobuf is a protocol from Google. The .NET implementation is again from the great guys at Stack Overflow.
It's really fast. Message packets as well. They work in a little bit different way. Message packets perhaps easier to start with. And that's one we'll see here. When I run this demo, I run it on a local Redis instance.
Redis is a cache server, originally in Linux. It's single-threaded, so you want to think about it when you configure it. That you may want more of them. It's ported by Microsoft Research and you can install it using NuGet.
What I like about it is that we can easily...
It comes with a command line interface. I can connect to the cache, I can see the stats on it.
We can see the keys in there. And we can add and delete keys on it. You can of course connect to your Azure caches as well if you run Redis in Azure.
If we try to call this service postman... We've used the binary formatter when we do this.
I have a modified version here of my call. It will return 2000 people. If we try to load our system with that, remember that we don't have any network latency. What we're seeing now is that it's out of process, but it's still on one machine.
It's now running pretty bad.
Then we can hopefully get some better results if we use message back.
Yes, we could handle more requests. With a different serializer.
Code for that is quite simple. When we work with Redis, since it's single-threaded, you want to have only one connection to the server. The client library will multiplex and pipeline all the commands for you.
But it doesn't make sense to have more connections. The server won't process out of them anyway. When we look at the add for Redis, it's quite simple. The only difference is that we need to serialize the data.
There's an interface called iItemSerializer. The same when we get data, we need to deserialize the data we get. If we have the binary format, nothing magical going in here.
Just taking the binary format, serializing it using memory stream and not with it. And same with message pack. Use the message pack serializer. This one caches the types for you. This makes the serialization fast.
And use the memory stream as well. So, how does that look in production? Quite good. We see we have a lot of cache hits. We like that. A few cache misses.
What's worth noting here is the network bandwidth. When you use a Redis in Azure, you normally see in the pricing, you pay for size, you pay for redundancy. You pay for if you want to have local storage or just live in memory.
But you also pay for network bandwidth. And they're not really clear on how much network bandwidth you get. It says you get low network bandwidth, medium and high. And suddenly things start crashing. And that's probably because you've hit a network bandwidth issue. Or it may be.
That was our problem. So, to solve that, we wanted to implement what we had with the managed cache. That we have a local cache and a central cache. So it works a bit like this. The application calls local cache.
If it's not there, it will fall back to the remote cache. If it's not there, it will execute the method from the application and call the slow thing. AKA your repository. And all the way up it will add the data it received to your caches.
And our implementation of that is called a double cache. Again, we implement the same iCache aside interface.
This is, in pattern terms, I would call this a decorator. It takes a local cache and a remote cache implementation. When you add something, it's simply just added to the local cache. Which would be the memory cache we saw earlier.
And it will then also add it to the remote cache, which will be the Redis cache. When we get things, it will first try to get it from the local cache. If not, it will fall back to remote cache. And those two will be responsible for adding the item to themselves.
So we still have one problem with this. And that is that the caches will be out of sync.
Since the data we'll have in the local cache will depend on when you got it from the remote cache. To solve that, we can use a feature in Redis called PubSub. It's a really simple way of distributing messages.
Redis is used in a single R, for instance. They use Redis as a backplane. When you have multiple single R instances, they use Redis to communicate between the similar instances. The same goes for Socket.IO, for those of you that use that node. What happens is that we store the data in Redis, we push the notification, and that is published by Redis to all the subscribers.
And then we have the local cache on each web instance to update their data based on the server.
So now it gets a bit more tricky. We have an iCache publisher. A simple interface just tells to notify that I have updated a key and the data type.
We need to pass the type of the data since the cache will be responsible for deserializing it into memory and it will not know the type. And then it also unnotifies the lead, so if something is deleted from the cache, we can do that.
We then have the cache subscriber and it will take an event handler for when the cache is updated or deleted. And it also has a method to get data from the cache which is used.
And then we have the implementation of this. A publishing cache which takes the cache-aside interface. It also implements the cache-aside interface. It takes the remote cache, which will be the Redis cache, and it takes the cache publisher.
When something is added, we add it to the cache, we then notify that this key has been updated to all subscribers. If you look at the GET, it's just a normal GET.
If we invoke the method, our repository, we will also notify that we have updated the local cache.
And at the bottom here we have the... Let's see... That's the publishing cache, let's see... And then we have the subscribing cache. This will wrap the cache-aside interface again. It will wrap the local cache.
It will cache the deserialized types. And it will attach this on the subscriber, these two methods. And when we get the message that the cache is updated, it will get the data from the remote cache,
check if there's a time to live or not, and add it to the local cache. And this means that all our local cache instances will be in sync. And it also means that we can, from other processes, forcefully invalidate and update our cache.
So if you have a messaging infrastructure, and you get a message that some data has changed, you can proactively invalidate your cache. We had one problem with NRK when there were so many layers of cache
that we could use an hour for something to time out, because we didn't have any invalidation. And that's a problem when you reveal the winner of some reality show by mistake.
Then somebody gets panicked. So it's really nice to be able to invalidate, forcefully update the cache when something changes. Let's just see how that can work.
Here's our PubSub cache. So notice that, again, we have the really fast response time. Since we do hit a local cache, we don't hit the remote one. We have our foreign mister here. I will not pronounce that.
And then we can... I have another small program. And it's called the same service.
Called our cache, updated it, and sent the publish message. So we find now here, hit it, Mr. Jake Brown. And this is really, really old.
And what happens when we start to use this is that we see the... This is from one of the servers, we see some performance counters. We see that we do have a lot of local cache hits. We do have some misses where the data is not in the local cache.
And then we hit the Redis server. And all of these will go to the database or some backend service. And this is really what saves us, this part.
Now we can of course go wrong. I'm not saying that you should put all your cache in the local cache. But you have more memory in the machine than you think. And we took all the TV shows at MRK.
And when we serialized them and stored them in Redis, that was about 700 megabytes. Not the streams, but only the metadata. If we took only the 40-60,000 active TV shows where we have the display rights, which you can actually watch. It worked down to 300 megabytes of data.
So we can in fact keep a lot of our data in memory at all times. That's a good thing. And the really good thing that happened is that the network bandwidth dropped. Before we had this local cache wrapping the remote cache,
we were up to 2.7 gigabytes per second of traffic on our Redis instances. You won't get that normally. I think they were nice to us since we were running Redis before it had gone in general availability.
After we added the local cache, it dropped to 6-700 megabytes of Redis.
It wouldn't go around if we had these high numbers. Any questions on this?
Let's continue. I have some goodies in hand. Redis is more than just a key value store. If you use it only for key value store, I think you're perhaps not utilizing it completely. I recommend you all to search for a document called an introduction to data types and abstractions.
It's really nice to read that one and try to mentally map that to how you can use those data models in your application. There you have key value store, which is the normal one used for cache, but you have lists.
You have hash sets. You have sets and sorted sets. On the sets, you can do the intersection between things. And then you have hyperlog log, which is used to count really, really large amount of things. You need to count the number of uniqueness. And then you can also do geospatial searches in Redis.
We use this, and then you have LuaScript. Oh, sorry, Norwegian text on that one. You can execute script on the server itself. It's an atomic operation.
Since you are running Redis single-threaded, only one thing can happen at a time, and that gives you the protection. So we don't need to think about transactions. If you wrap it in the LuaScript, it will run in that sequence. The scripts can be uploaded to the server, and you just call them by calling a hash.
At Anarkay, we use it for the now playing data. To sort the non-playing data, we use a sorted set, and then we keep the song, title, and metadata in the hash sets.
And to add an item to it, we use LuaScript, where we have a channel ID. We get in the program, the ID for the current song, when it starts, some JSON data for it.
And how long it should live in the cache. So first we add it to the sorted set for the channel.
And then we add it to the hash set, the JSON data. And then we purge expired items. We don't want to keep all the tracks in Redis, we only keep what is available in the live buffer on our streams.
And then we read it, just get the sorted list, and then we look up the elements in the hash set to sort them. A word of caution, if you're going to use LuaScript, if they get too big, they take time.
And since this is single-threaded, if you start using a lot of time on your Redis server, your cache will be slow. We've been quite good on that one as well. Didn't really see how much we were tormenting our servers until things started to fail.
That's not a fun thing, but one experience we've learned. And in Redis you can see the worst performing scripts, so you can log into the server using command line. And it's like a slow blog, where you can see the slowest scripts you have.
And that's it! Yes, between cache items. We have some, that's handled by LuaScript.
That's the way to do it. You can cache byte bulbs, that's why we take the object memory and serialize it to some bytes.
Yes, you can choose not to. We had the object, so we needed to serialize them. Thank you all for coming!