Hello.
In this lecture, we're going to talk about Question 29, what is getting caching is a mechanism that
allows storing some data in memory.
So next time it is needed, it can be solved faster to understand it better.
Let's consider this class.
This class is responsible for retrieving a personal object from some repository using the person's first
and last name.
This code is very simple, but we must be aware of one thing.
We don't know what this data source is exactly.
And in our real life project, accessing the data from an external source may be slow.
Maybe the class implementing the repository interface connects to some bulky database or retrieves data
from some API.
It won't be the case that every call to this external data source takes some considerable time, and
if many calls are executed, the application may start to work slowly.
We don't want that.
How can we make it better done?
Well, one solution could be this if we already accessed the data for some particular first and last
name, for example, John Smith, we could store it in the application memory.
And next time when we want to find John Smith will access his data immediately instead of asking the
external system to provide it again.
This mechanism is called caching.
We store some data in a cache, so a piece of memory of the application making it available immediately.
Let's implement a very simple caching mechanism.
We'll start with defining a class.
As you can see, I mostly discuss generic so it can store and type in our case, it will store objects
of type person.
Now we need some kind of container to store the casperson objects.
What data structure would be the best for us?
We'll want to retrieve them by providing first and last names.
So perhaps the best choice would be a dictionary in which to pull off two strings for both names would
be the key, and the person object would be the value.
I used a value to TOPO for the key, because for dictionary keys, we want to have value based equality
comparison.
If I use regular kepu, which is a reference type two to taphouse holding the same first and last name
would not be considered the same key.
Both the dictionary, so our cash would not work as expected that we wanted to keep the cash generic.
So the dictionary archetype should also be parameterized.
As you can see, I also admit the not no constraint.
I did it because the dictionary keys should never be known.
Now about the get method.
First of all, it should take a key as a parameter.
Remember, in our case, this key will be the we're holding the first and last name.
We will need them to retrieve the proper person object from the repository.
If this dictionary already stores the value of a given key, it means it already has been read and cached
and we can simply return it without sending and the request to the external system.
But if in this dictionary, there is no such key.
This means the value is not yet cashed.
We never asked for it and we never retrieved it.
This must be the first time when someone asks for it and we need to access it somehow.
In the case of the people controller, it would be read from the repository, but we can't do it like
this.
This can't work because the cash class is generic, and it must work not only for people read from some
specific repository, but anything else too.
In other words, we must also provide some generic mechanism for reading the values, at least for the
first time, so they can be gun stored in the class.
The simplest solution is to pass a funk to the get method.
This funk will return an object of the value type.
It will be up to the color of the get method to provide a specific method of retrieval of data.
In our case, the people control will be using the cash and it will pass a function reading the person
objects from the database.
Do they get it?
We can now simplify this code.
What we ask for devalue under the given key for the first time.
It is not, it clashed.
We must it for the first time using the function that is provided as a parameter after it's read for
the first time, we can stop it in class and return it.
Next time, we'll ask for the value identified by this key.
We won't need to go to the external data source will simply return the cost value.
Let's now use the cash class in the paper controller.
First of all, we should declare and initialize the private cash field in this class.
Now we can use the cash in the good by name method, for now, this method doesn't use it and it goes
to the repository every time we ask for this specific person.
Let's try to use the cash.
The first parameter will be the key.
So the Tapu consisting of first and last name.
The second parameter is a function that we allow us to access this person's data for the first stone.
So basically what we've been doing here?
Let's test if it works as expected.
If it does, the good by name muttered from the repository should be called only once when we try to
access the John Smith object twice.
So here I am trying to access the same person twice.
Let's put the breakpoint in the repository.
If everything works correct, we should hit this breakpoint only once.
So here is the first and hopefully the only time when we reach to the repository, let's click continue.
And that's it.
We didn't see this break point for the second dome, which means the cached value has been used in a
real world application.
We could ask for John Smith hundreds of times and instead of asking the database for his data, this
many times will only ask once we implemented a super simple cache ourselves.
Of course, we could make this cache more complex.
For example, we don't ever remove anything from the cache now, and it may happen that during the execution
of the program, it will grow extremely big and will finally drain our application of free memory.
Usually, some kind of clean up mechanism is added that removes the data that is older and then some
specific time.
Also, this cache is not thread safe, so it shouldn't be used in multi-threaded applications.
There are, of course, some existing libraries that provide the caching mechanisms.
For example, we could use Norgaard to install Microsoft Extensions Caching Memory Package.
It works very similarly to the cache we implemented.
This is how people control would look like if we used Microsoft's implementation of the cache.
As you can see, it's almost exactly the same as our cold, and I highly recommend using this package.
Nevertheless, I wanted to show you how to implement a cut on our own so you understand how it works
under the hood from other cooking tools you should be aware of.
One of the most commonly used third party tools is called release.
It provides more functionality than a regular cash, and it's known for its excellent performance.
Before we wrap up a word of caution, caching is great.
But for some scenarios only if we don't retrieve the data identified by the same key repeatedly, but
we keep using different keys all the time.
It doesn't really give us anything.
You can always check cache success rate by adding a simple field to cache.
This counter will be incremented each time we use the cash, the data instead of retrieving it from
the data source.
You can track its value during debugging and see if your cash is successful.
The higher the counter goes, the better your cash is performing in your application.
So use caching when it really makes sense.
Caching is most often used to retrieve data from some external sources, but remember that even the
data are calculated locally can be cached.
For example, if your program does some performance costly mathematical or graphical operations that
take a lot of time, you could consider cashing those results to also remember that the underlying data
can change after it has been first retrieved by the cache.
It means the cache will be providing us with stale data.
That's why caching is best when the underlying data doesn't change often.
In this case, it's usually enough to have some mechanism that removes the piece of data from the cache
after some specific time has passed.
During the interview, you could be asked What are the benefits of using caching?
Caching can give us a performance boost if we repeatedly retrieve data identified by some key.
It can help not only with data retrieved from an external data source, but even calculated locally.
If the calculation itself is heavy, for example, it is some complex mathematical operation.
What are the downsides of using caching?
Cache occupies the applications memory.
It may grow over time, and some kind of cleanup mechanism should be introduced to avoid out of memory
exceptions.
Such mechanisms are usually based on the acceleration time of the data.
Also, the data in the cache may become stale, which means it is charged at the source.
But the old version is cached and used in the application.
Because of that, testing is most useful when you're doing data that doesn't change often.
All right, that's it in this lecture.
Thanks for following along and see you in the next one.