OK, so recording is the first slide. Awesome. For the recording, let's go. OK, so my name is Cristiano Hernando. I'm a senior Drupal developer at Factorial. I live in Belgium. I'm the maintainer of the group module. I'm happily married. Since about a year and a half, I've been the proud dad of a daughter. And I'm ruled by my cats. I'm also a highly sensitive person. I like to pass here for a second. This is something I always mention during my talks. Is anyone else here in the room highly sensitive, by the way? Raise your hand, please. Sucks, doesn't it?  Literally, you're always anxious. So I may get a panic attack during this session, or I preventulate, or whatever. I'll try not to, but I can't make any promises. So I always like to mention this, because it makes me feel a bit safer that the crowd knows this. Also, before we get started, I'm actually not a caching guru. I just spend a lot of time reading the course caching layer. So yeah, don't get your hopes up if you have any questions regarding varnish or something. That's not me. I just know the core side of things. So moving on. What is the goal of this session? The goal of this session is basically to inform you about new, or quote unquote, new features in Drupal Core. They may have been around for a couple months already, but still relatively new compared to when Drupal 8 first came out. I'm going to provide you with some tips on how to improve your code using this new technology and give you a sneak peek of what's to come in the near future, or almost near future. We're going to start easy with some really simple tips. And then we're going to end with massive confusion of people questioning what is still real. Like, honestly, there's some very exciting stuff at the end, but hopefully it will all make sense. So let's start with the memory cache. I'm pretty sure most people in the room might have seen the change record for the memory cache, but just to quickly go over it. What is it? It's a new cache. Again, air quotes. It's derived from the existing static cache. For those of you who've written tests for Drupal Core before, the static cache was one of those in-memory caches that we used to quickly test the behavior of caching without having to set up a database back end. So this basically mimics the functionality of either static properties or properties on a service, because services are usually only instantiated ones. So they're properties, kind of, Akmik static properties. This cache does perform better than the regular static cache, because it's a regular static cache used to mimic the database cache. And it does so by serializing everything that goes into it and serializing everything that goes out of it, so that if you request something from the database, it's a new object. The static cache used to do the same thing. Now, the memory cache doesn't. It's basically a channel of the existing static cache, but it removes that serialization so that it's way faster. And it supports cache stacks like any other cache. So that's a big thing that we'll discuss later on. You can read the full discussion about the introduction of this cache on that URL. You don't need to take pictures. My slides will be online. The links are clickable, so please just enjoy the session while it lasts. Although it's rather poorly named, there is an issue about that too. It's called the entity memory cache, but you can basically use this for any in-memory cache. You don't just need to use it for entities. And it's not implemented as a proper cache by default by the entity API. Just to show you, is this readable? Yeah, it is cool. So it's defined just as a service, but nothing else. So this means that in the entity classes, you'll notice that they still need to manually invalidate their cache tags and everything. JSON API, for instance, ran into that. They had this issue on Drupal.org. Turns out, as some of you may know, you need to tag your cache pin so that the cache tag invalidated will pick it up and then all of that automagically cool stuff happens when you try to clear a cache tag. So here, and it also doesn't have a cache factory. That's not necessarily a bad thing. You don't need one, but most of the caches in core do have one. And it's useful in some cases, where you can just define the bin that you're looking for, ask the factory for that bin. So I would have preferred if it had one, it doesn't. So this is how the original static cache was defined, and you can see that it's a lot more verbose. But yeah, so there is still some work to be done there, but it's functional, it's there. So when do you use it? If something inexpensive is being calculated often, and you'll see that core has already solved this problem with this pattern, you see this a lot in core. Just take a quick second to look at it. We set like a property on a service. If it's null, then we try to calculate it if we have it, we start returning it from that point on. Now there's a problem with that. What if outside code needs to clear this cache? Or what if the calculation that's being done depends on something? You wanna add those cache tags. Now yeah, static property doesn't really know much, doesn't really do much. So you end up seeing a lot of these classes that use this pattern defining a clear cache method or reset cache or reset index or whatever. Like it's littered all over core, and this really sucks because then any instance that might need to clear that cache needs to manually think of the fact that that cache needs to be cleared to implement that code and it's just sloppy. So let's stop using this, please. Oh, by the way, this is like the first icon I found when I typed cross. I just wanted to have a cross across it, but I found this to be cool one. So yeah, har har mateys or something, whatever. So this is how you use the same pattern with that memory cache. So basically you define your cache ID and then if it's in the cache, you return that. If it's not, you set it. Also, this is just a simple example so that it would fit on screen. Around these parts, you would set your cache tags if it depended on something that requires cache tags, but it's an advanced station, so I do expect people know how to use a cache back in Tindra.core. So yeah, there is an advanced use case you can have for this as well. So what if you have expensive calculations? So not just something simple, but something that is actually really expensive to calculate. So after the request, you still want to have that data. So then you can use this pattern. So you combine the memory cache with a regular cache, which persistently stores it. And then as you can see, you first look for your memory cache. If it doesn't have it, you look in your persistent storage. If that has it, you return it, but you set it in the memory cache. If none of them have it, you do your expensive calculation and store it in both. This is a very cool pattern, but don't do it. Like it's weird that I say, but don't do it because we actually have something really cool in core that does this for you. So what we have is we have the backend chain. And the backend chain behaves completely the same way like API-wise. It implements the cache backend interface as well. But it allows you to specify any number of cache backends in a very specific order. So it's important to have this order right. And it will do exactly what I showed you here. So it does that for you. So you don't need to write any code there. And then when you try to access your, in this case, JSON API resource types cache, you just access it like if it were a regular cache backend. So this is something that if anything you wanna take home today to improve your caching and you have expensive stuff, start using this. Like replace every cache you have with the memory cache in front of it and then a regular cache behind it. It really improves the speed of your code. Second thing I wanna talk about is you should start caching smaller. This is a lesson I've learned from the render cache. I've read it from like A to Z and Z to A for a particular reason, which I will get to later. I'm not a seto mesochist or something, but I really needed to read like the render cache. And yeah, I ended up seeing this and I thought, hey, we can use this. So when building something complex, cache the smaller parts of it. Render cache does this really well. So when you're building a render array, you have your cache keys at the top. But then anyone can start adding stuff to that. And it is actually your job that if it's expensive, you either provide a placeholder or a lazy builder, but you also set cache keys there. And basically what happens in the render cache is as it's going over the children, well actually in the renderer, this happens. As it's going over the children, it looks for every child that has cache keys and then says, hey, render cache, do you have this piece of information cached separately for me? So imagine if you wanna calculate the distance between Earth and the Sun live for anyone, but there's other information on that page that may vary, then you don't wanna recalculate the distance between the Earth and the Sun for every single visit. No, you wanna cache this little bit and then maybe the rest of the page is easy. So it's really up to you to figure out which parts or hearts are very dynamic and then in case of the render array, you would assign it different cache keys. Don't overdo it. You still need to make that consideration like it's looking up this information, the round trip to the database, more expensive than me calculating it or not. If you overdo this, you end up with a million round trips to your database and then your performance is still very shitty, so please don't do this. Too much, by the way. So there's an example usage in group just to give you an idea how you can use this. So not to go into too much detail about the group module but I have basically three layers of permissions. I have the anonymous, I have what I call outsider, it's just authenticated users and then I have the authenticated users who are part of a group. Now in that order, the permissions become from very invariable to very, very, very variable. So I have this cache context which needs to return a hash of all of your permissions. If I were to store all of your permissions in a cache, then I may as well not cache it because it's so dynamic. But I realize that two out of three parts are actually pretty much cacheable and just the latter parts, in most cases, is cached per user. So what I ended up doing is I have these three methods, one for anonymous permissions, one for authenticated and one for the member permissions and each of them are cached separately. So the anonymous cache has only got one entry, the outsider cache has got maybe five or six entries and then you've got the member cache which has a lot of entries. But when I need to calculate your permissions, I speak to all three of these caches and the last one is very likely to have a miss but the first two will have hits. So I only need to calculate the smaller subset of your permissions again to be able to give you the full hash. So this is another takeaway. Like, you know, try to consider what you're caching and if just the small part of that is very dynamic, stop caching that as part of the whole. Like start caching the rest separately and then cache the very dynamic part separately. So this was the easy part of the presentation. I just wanted to warm up with a few tips. Okay, yeah, so yeah, I had this slide because I expected that. So no really, this was the easy part. So let's talk about a variation cache or the fact that we want to use cache context everywhere. So in core, there is a difference in how we cache things. We have two backends basically. We have the regular cache backends we just saw and we have the render cache. And if you want to set something in a regular cache backend, we need to provide on top of the data three items. A cache ID and expiry dates optional but you can provide it and cache tags again optional. If you want to set something in the render cache, we have a system that I like way more and it's cache keys and max age, cache tags and cache contexts. I'm pretty sure everyone here is familiar with that but if you were ever wondering why render cache has that nice system and the cache backend does not, I'm about to explain that. So the thing on the right gets converted to the thing on the left. So the max age becomes an expiry date and the cache keys and context are turned into this long cache ID where the keys are concatenated. First, followed by the context with context name equals and then the active value for that context. Why only the render cache? So why does only the render cache do this? Well, it's the only place that has cache redirect handling. So more on that later. It's optimized around predictable base cache ability. So this is this pre-bubbling thing that you have in the render cache. You know what the cache context are on the very top level parent. So instead of asking the cache for just the keys you immediately know, well, we're going to vary by these contexts. So it has this optimization in building. And due to time constraints, basically it was never finished as a proper cache. So it's all like hard-coded into the render cache. All of these behavior with cache context is hard-coded into the render cache. Which leads to some very questionable solutions. So here's an example of some questionable codes. So the dynamic page cache is this new cache in Drupal which allows you to cache pages way better than before. But it caches based on cache context. But it's not a render array. So how does this happen? Yeah, take like five seconds to read this while I drink my tea. Yeah, I was thirsty. So basically we have this thing sitting at the top of that class. And I was like, whoa, the first time I saw this. Like what the hell's going on here? So let's look a little further. What happens when we set something in a dynamic page cache? Response to render array. I was like, what? That method literally puts the response in the render array and stores it in the render cache. That's how we achieve dynamic page cache. We store responses as render arrays in the render cache. Yeah, not perfect. But maybe we should look further in core. Like this might not be the only problem. So let's look further and I found another use case. Variable cache IDs. I'm using air quotes a lot. Like there's nothing filming me so people at home won't see that. But variable cache IDs. So just to be clear, a cache ID can have variables. It's not that it's a crime to have variables in your cache IDs. That's fine. But those variables should really define the thing itself. So if you are rendering node one, regardless of the page you run, then it's okay to have the ID which is a variable in the cache keys. But they should never define a version of the thing. So if you are rendering node one for people with a given permission that can edit that and therefore the node has edit links, you don't put the fact that they can edit that in your cache keys. So yeah, example, the cache key for nodes leads, one, three, three, seven is that specifically but only if you really want to see that node. Yet because we lack cache contexts everywhere, people create their own variable cache IDs and this is like really bad. So let's look at the route provider in core. The route provider is supposed to give you the active route collection for the current requests. And yet it receives a request and starts adding stuff from that request and the language in its cache ID. Weird. It makes sense now because we don't have cache context but still this is weird. So the thing that itself is the route collection, not the route collection for a specific request. There's never a scenario where we're on request A and we're telling Drupal, oh hey, give me the route collection for request B. Like why would you do that? So you're actually caching the route collection. So this should just have the cache key route collection and the following cache context. It varies by the URL language, it varies by the URL path and the query arguments. So that's what we should be using. This becomes a problem if other modules need to affect that code. So look at the domain access module. This is by the way a code from the domain access module. So because we don't have those cache contexts and because the domain access module makes all of your content, pages, whatever, also vary by what domain you're on, they need to make changes. And as you can see in the middle part, it's really easy to make those changes if something depends on cache context. All they need to do to make sure that your entire website runs using the domain as a variation as well is just add URL.site to the required cache context. That's all they need to do. It's really awesome if you ask me. Yet we have that route provider class and they were making their own cache ID. So they need to change or swap out that route provider class with their own. And they do that and then they just add request gethost to the cache ID. So we didn't have request gethost here and then they just add it. Now, this is not sustainable because what if more modules need to do this? Like there's only one module that can swap out the service. Then you need to start decorating.  It's really annoying. And you're not always this lucky because in this case, the route provider has a method dedicated to give you the cache ID. But more often than not, the cache ID is just a string which is set as part of a very large method which does the expensive calculation. So now you need to copy paste all of that code if that code later on has a security issue or whatever. You don't carry that over. So this leads to some very sloppy codes. So let's talk about these cache redirects that actually power cache context. They're at the heart of the render cache right now. And I'm just going to talk about them in a nutshell. I wanted to have an Austin Powers quote in here, but I couldn't find anything but a GIF and that's distracting. So yeah, this is me in a nutshell in a moment. So eventually they do lead to these variable cache IDs. So they start from your cache keys. But as I mentioned earlier, they then convert the cache context from the active context and then add that to your cache ID. But when we try to retrieve the data that we set before, we only have the original cache keys or ID to go on. And this is why cache redirects exist. So what we do is at the original location where the cache keys lead us. We store a redirect that says, you know what? I'm not giving you the answer just now, but I know that for the answer to be found, you need to tell me what the current values are of a set amount of cache context. And then you should look there. So it's basically like a treasure map that sends you on another journey. And basically this quote says it also, when I last stored this item, it varied by x and y. So in order to find it, we need to build a cache ID with the active values for those contexts. So I have this example. So suppose you have this hotel that allows you to bring pets. And we know that you are either a person with a pet or without a pet. So this is our pre-bubbling cache context that we already have. So at the front desk, there is someone that asks you, do you have a pet or don't you have a pet? In case you do, don't have a pet, just grab a regular room. In case you do have a pet, I need to ask you some more questions to find a suitable room for you, whether it has an aquarium, a scratch ball for a cat, or whatever. The fact that you have to grab this list and ask them some questions about your pet, that's a cache redirect. That is exactly what that does. It's like, I have this extra list of questions and then I can help you. So the render cache implementation of this system is optimized because we know the pre-bubbling cache logic. So this is what I said.  And that desk was like, yeah, I first need to ask whether you have a pet or not. And you can go to that desk and they'll help you with that question. And that's silly. We already know that we need to ask that question. So that is that optimization that we have. There only one redirect is ever stored, though. And this is really crazy. So what that does is it basically says, you might vary by this or that or both. But I really don't know. So how about we just look up all values? To go back to my hotel situation, suppose I have a guest coming in. And my first question is, is your dog a salt water or a freshwater dog? Oh, no answer. OK, does your fish need a scratching pole? This is what we're currently doing. So the redirect that we have, there's only one. And that one keeps growing and growing and growing and growing. If someone comes in my hotel with a dog, we tell the system or the guy at the front desk, like, ask questions about a dog. If next someone comes in with a fish, we tell them, oh, and also ask questions about a fish. Yeah, but what if he has a dog? No, no, ask questions about a fish. It might be a fish. OK. So this leads to some problems, as you can imagine. The redirect keeps on growing and growing and growing. But because it's such a very volatile redirect, we tag it with everything we can imagine. So if a dog has cash tax, we add those. If a fish has them, which obviously leads to the fact that this redirect is invalidated very frequently. And this causes several problems. One, we have it to take care of caching. But because it's invalidated so frequently, we basically don't cache highly dynamic things. You can test this in core. With given render array structures, you'll notice that they almost never really come from the cache because they're always invalidated. And it also limits how we use certain things in core. Here's a quick story from the group module. At one point, I had a very annoying thing with trying to show lists. And I wanted to make sure that a list would vary by what group it belonged to. So I tried to create this cache context, which you could pass an argument like we have for the user.rolls cache context. You can ask, does this given user have a particular role? And I tried to add that. But because there can be a very large amount of groups on our website, we would have a very large amount of possible parameters to give to that argument. So I asked around some people who maintain core. I asked them, is this a good approach? And they said, no, you can't do that. Can't is like a very strong word. Why can't I do that? If you do that, some pages will literally become one, uncacheable, and two, actually get a performance hit. Why? Yeah, because of cache redirects. I didn't understand cache redirects at the time. And I was like, OK, this sounds like mumbo jumbo to me. I don't know what that means. So I'm going to trust your answer. And I developed the group a different way, a very eventually more powerful way, but different. But just to give you an idea of what this means, suppose you have your local actions block, your tabs at the top of a page. And you have this system where you can subscribe to a node or not. And based on whether you're subscribed or not, that tab says, manage your subscription or subscribe. You would want to vary that page based on a cache context, which just accepts the argument, a node ID, and then whether or not you're subscribed. You can't do that. Because if you do that on page one, then you add this cache context for node one. If you do that on page two, you add it for page two. But all of this gets crammed into that redirect.  And your page just crashes. So this is why they told me it's not possible. And I was like, excuse my friends.  The group module is insane. In that way, I'm always running into the limits of core. And this was really a limit that I just could not live with. So what if I told you, I fixed this? You can be serious, is probably what you're thinking. No, I am serious. I spent the last seven months fixing this. So introducing the variation cache. So what is the variation cache? Variation cache is something that abstracts everything that we saw in render cache into a separate cache system. So the render cache became way smaller. We immediately updated dynamic page gas subscriber to no longer convert your response into a render array, but just use the same cache. And both of those changes didn't break core. All tests go green. It handles redirects differently by using a cascading system. So now in my hotel, if you come to me with a dog, I'm like, what type of animal do you have? A dog. Oh, I have a question list for a dog. Or if you come to me with a fish, I have a question list for a fish. But if it turns out that there are more variations, and then we have a saltwater fish, I'm like, OK, it doesn't need like a small tank or a big tank. There is more questions as you go. So it's basically the same optimization like we had before. It accepts your initial state. So there's no optimization or performance loss initially. If you have pre-bubbling cache context, it does accept that. And it immediately starts building its cache IDs based on that. But then as you can bubble up cache context, so do these redirects learn from them. So sometimes it introduces a cache miss because the system is teaching itself. But eventually, it leads to a full tree of potential redirects which never have to be flushed. So you get like a full tree of cache redirects, no types attached, no expiry. Because once you get a miss, the system will start self-learning again and potentially alter the tree. It's really cool, actually. I'm really proud of that part. And to show you that it works, it's already part of the group I go. So I was done, like this may sound really bad. So don't take this the wrong way. I was done waiting for stuff to get into core. So what I did was I created it. I wrote tests for it. I have it in the issue queue. I discussed it properly with core maintainers. They're excited about it. They really want to get it into core, but they're currently really busy with 8.8 and 9.0. So Wim Lears is lyrical about this. He really wants to see this go in. It's Dev Days in Ghent next year. I'm hoping to have some time with Catch there because apparently he's going and he's not here at this event. So Wim already told me this is getting into core. We just need to find the time, but this is getting into core. You can read the full patch here. And reviews are more than welcome, but I do warn you, I think the patch is about 150 kilobytes because, well, it's a lot of code to move around and then some tests needed to be adjusted because all of the old tests were putting stuff in render arrays and putting that through the render cache. And yeah, basically, all of the cache IDs which we hard-coded in all of our tests needed to be converted to cache keys, and that tends to add up to a lot of kilobytes of just useless cruft that makes the patch very unreadable, whatever. So just to give you a sneak peek, because I don't want to just tell you, hey, I fixed this and that's it. I just want to give you a sneak peek. So what does this look like right now? This is the same code that we saw earlier in the dynamic page cache subscriber. So we don't change anything anymore. We just say the cache case response. We put the response directly into the cache so there's no converting to a render array or whatever. We grab the cache metadata, as you can see above, from the response and we add our persistent cache context, which you saw in that weird render array earlier. But our initial pre-bubbling state was just those cache contexts, and that's it. And if you look at the render cache right now, this is what it turned into. Sets in the render cache is just this now. So it works for everything, also render arrays. So you just grab the cache case from your render array. You set the data as the cacheable render array. That method just moves some stuff around, but nothing too serious. And then you just create cacheable metadata from the render array and add the cache tag render because that's what the renderer does. And you get the pre-bubbling elements, and that's it. I think I went, wait. No, actually, 10 minutes is fine. So any questions? I was seriously worried that I'd have 20 minutes left, but it's only nine, so yes. I think there's a mic there, or I can repeat the question. This is super cool. Because all of those services should be swappable, have you thought of, or have you already put this into a contrived module to sort of shim this into core without having to patch core? No, actually I haven't. And reason is I can put this into core already. So I could swap out the render. Yeah, I could swap out both, but actually that's not really a bad idea. Let's do it tomorrow. Yeah, I'm still here tomorrow, but I can already prove that it works because the whole patch goes green, and then JSON API wanted to add this whole new thing to core, and I so happened to stumble upon their patch, and I saw that they were putting a lot of their responses into a renderer, and I was like, oh God, no, please don't add another thing that I need to alter later on. So I took their patch, and I said, you know what, I'm just, for shits and giggles, going to add my code to it, and see whether that still goes green, and it went green. Like from the first try, it went green. Like this huge JSON API patch that I didn't understand what was happening there just went green. I was like, okay, I came up with something really cool here apparently, so it's working everywhere. But no, I haven't done that. I should have done that. You still can? Yeah, I still can, but as I mentioned, it's part of the group module now, and the way I did it was just because I needed to get moving on with some of the stuff that needed that I just have this core fixed folder in the group module now, where all of this resides with a big ass warning saying, yeah, this is internal, I'm using it, don't use it, it will get into core someday, and then we should really just start using it, but please don't use this code yourself. I should have done that and made that a dependency. Where were you like two months ago? No, okay, anyone else? No, did, like, not or whatever, if you, did everyone like enjoy this session? Like, because usually if there's no questions, I start darting like, was it too difficult or something? But the first part, those tips, were they helpful? Cool. Second part, did that blow your mind? Awesome, then I have nothing left to add except maybe join us for the contribution opportunities and what did you think and then there's no more slides left, so yeah, so again, reviews, welcome, like, read the whole thing, but yeah, if you're not very familiar with the render cache, half of it might not make sense, but just do nitpicks, piss me off, like, oh, you forgot whatever there or something, but more reviews means more traction, means more stuff is happening, so if you wanna review it, please do. If not, no hard feelings, I'm starting to get used to the fact that sometimes getting things into core is a very lonesome journey, so. All right, that's it. Can I press the red button again to make this thing?


Hey, everyone. Thanks for coming by. My name is Shin Horiyuchi. I'm with Trilio Data. We are backup recover managed for OpenStack.  So in a cloud environment, what you'll see here is, why is cloud native backup important? What you'll see from, obviously, everyone's journey into the cloud is that you'll try to use your legacy data protection tools. And you'll see that, obviously, they're agent-based, heavy, hardware-dependent. You've got vendor lock, obviously, some scripting involved for specific types of applications. And then you're locked into a central IT and ticket-based system for support. And that really isn't adhered to anything like the cloud. So what's important to cloud native data protection is that we want to be able to leverage your native OpenStack APIs, software only, vendor agnostic. So you're not locked into anything. Automated integration, and then really from a cloud perspective, OpenStack perspective, is that we're really focused on self-service management via tenant-driven control. OK, now, how we can help or how we help the OpenStack journey to come to completion is that, number one, we're agent-less, focused on multi-tenancy, self-service model from by default, scalable as your cloud grows, we'll grow with it. We're non-disruptive, and we're OpenStack integrated. Number one important thing there. But Trilio is actually not just data backup and protection. It's beyond backup. And what I mean by that is the recovery. Backup is really only half the battle. But when you get into the recovery, that's where we really shine. So from a local backup recovery, we have file and folder recoveries, virtual machine recoveries, entire tenant workload recoveries, and then also disaster recovery scenarios there. Then on top of that, a couple of use cases here from obviously from an OpenStack perspective, depending on where you are in your journey, tenant-to-tenant migration, availability zone migration, distribution migration, and version migration. Now, I'll go a little bit deeper into these as we get through a couple of the slides. But Trilio really integrates with everything that is OpenStack, meaning we leverage all the native OpenAPI services, a Neutron, your Novas, your Cinders, your Glances. We are compatible with all the different distributions. And then from a target repository perspective, again, these are the different types of protocols that we support. And what really differentiates us from anyone else out there in the marketplace is that the way that we capture the data. We not only capture the data, but the metadata of the full tenant's workload. And what I mean by that is we'll leverage your OpenStack native APIs to get the metadata of your VM. So for example, we'll get your networking information from Neutron, your VM information from Nova, your volume configurations from Cinder, and then your Glance images from Glance. And then we capture everything in an incremental, forever fashion. So a couple of things here from a use case perspective. So we've covered backup and recovery. So we have your local VM recoveries, backup and recoveries. We have our file and folder recoveries. We also have our tenant recoveries within the cloud. And then once we get into the kind of fun stuff here is what else we can do beyond backup and recovery is the migration perspective. Everyone has a different perspective on how they can use the cloud, how their journey has changed throughout the years. And so from a tenant workload perspective, what we can do here is backup from a tenant within a specific cloud, and then restore that to a new tenant. So for example, if you have a pre-prod tenant, do your testing there, do your backups, and then recover or migrate that to a new tenant within that cloud and call that production. Another idea here is take a backup from tenant one, import or migrate the workloads to a new cloud tenant as well. Another use case here is availability zone. Let's say, for example, you have AZ East, AZ West. And AZ East is starting to get a little saturated. You want to start to do some load balancing from a VM perspective or a workload perspective. You can pick and choose a couple of workloads to say, hey, I'm going to move these over from AZ East to AZ West to load balance everything across. And then from an upgrade or OpenSec version migration, let's say you're on Okada, you're looking to migrate over to a new cloud on Queens. Same idea, backup everything from a tenant workload perspective, restore that back into a new cloud on Queens. Same idea, distribution. So let's say you're on upstream and waiting the waters there saying, everything looks good, but now I want to jump in the boat with one of the main distributions here. You can do that type of migration as well. And then one last thing here is disaster recovery. So let's say you have OpenSec Cloud 1 and 2. And 2 is kind of you have that cloud stood up ready to go. OpenSec Cloud 1, you've got Trilio Vault as the backup solution. You do your local backups here. You would replicate the target and then recover into that cloud from there. So that kind of use cases, backup recovery and use cases there. So we are in booth C2 right there, kind of a little hike from here, but I'd like to get into a demo from here show you what it looks like from a OpenSec integration perspective and then a demo of a tenant migration. So I'll log into this cloud here. And so what you'll see here from a tenant level perspective and our horizon integration, you'll see that the new tab here is exposed called backups. Within backups, you'll see something called workloads. Now, a workload to Trilio is a logical grouping that answers your four main backup questions. What do we want to backup? When do we want to back it up? How often do you want to back it up? And how long do you want to retain the data for? So if we look at one of these workloads here, you'll see Cloud Bronze. And if we look here, you'll see that there's three VMs that are being protected. And then the policy or the protection scheme tied to that is right here. So you have your start date, start time, end date. You repeat every RPO. How often do you want to back it up? And then your retention policy can be set in one of two ways. As you can see here, either the number of snapshots to keep or the number of days to retain a backup. So if we go back in here, we'll take a look at these snapshots here. And as you can see, these are all the backups that are available for recoveries. And as you can see here, if you go to the first, it's a full first and then incremental forever fashion backup. So each one of these is a synthetic full backup that is fully mountable, fully accessible, and fully recoverable. And if we look at one of these backups here, what you'll see is not only the data that's being backed up, but the metadata of that VM or the tenant's workload backed up here. So VM 6, 10, and 8, you'll see that there's IPs that are tied to them. We'll get the security groups, the flavor of the VM, any networks that are attached, any volumes that are attached, data volumes, and then the ID of the VM. Now this type of data is captured at every backup. So for example, if someone adds a new NIC or changes the network type or anything like that, we'll capture that at every backup. I focused a lot on the backup side of things, but that's really, again, only half the battle. So if we look at different scenarios from a local recovery perspective, your worst case scenarios, these VMs are gone. Someone has deleted them. So to recover these, you use a one-click restore. Now this one-click restore will take this specific point in time backup and restore it as of this configuration back to its source location. The other types of, the more granular level of recoveries are what we call an in-place restore. And this gives the tenant or the user the ability to pick and choose specific volumes out of those VMs to recover. For example here, if we take VM 6 and we say, all right, this VM isn't booting, but we think that we believe the data disk is fine. We take it, we can click here and say, restore just the boot disk. If we go to VM 10, for example here, we say, all right, it boots fine, but the data disk is corrupt, let's restore just that. And if we go to VM 8 here, we can say, all right, this guy's fine, don't touch it. So that's back to the source location. Another granular level recover you have here is selective restore. This gives the tenant the ability to use a copy of a production VM, for example, in a pre-prod environment or a testing environment for test devs. All you can do here is change the network mapping of the VM to maybe like a private internal network or something like that. Change the volume type mapping. So if the destination has a different type of storage, you can use that. And then again, at a per VM level, let's say for example, we wanna test VM 6 as a production VM and you wanna copy of it in a different availability zone. We can do that here. We name the VM. You can change the flavor of the VM. Then you can actually stand this VM up in a new availability zone. Again, test dev availability zone, anything like that. Test it. Again, VM 10, same idea if you wanna rename it. Get a copy of it. Change the availability zone. All right, so it's a really useful tool from a test dev perspective. All right, and then the most granular level of recovery.  So that option, the last option here is called the mount. And what we wanna do here is also show you a file search. So let's say for example, VM 10, we're looking for, someone had modified the Etsy host in this application, so it's not talking to the other servers, IP was changed, anything like that. What you can do here is search against all the snapshots, a list of maybe the last three snapshots here or specify a specific date range. And what we can do is search against the backup images that we store, and the search criteria will show the results. Now, after the search results come back, what you'll see are the three, whoops, the three images from the search criteria. And if we expand all, you'll see the search results here. So you can see your modified time, access time, and the file names from the search. And then from there, they can identify the backup, where the file is that they wanna access, and then go in here and mount that snapshot. Now, I have one mounted already, but the process would look like this. If we go to mount the snapshot, you'll choose what we call a file recovery manager. Now, this is a VM that is a public glance image that we would ship to the Cloud Admin and make it available to the tenant, so that they would spin up this VM and mount our backups to, kind of acting like a proxy host. So once that's been mounted, what you would do is browse to that file manager VM, and what you'll see here is, since we've mounted the entire backup, you'll see all the data that's associated with that backup. And then the user can go in, drill down into the folder, to the file that they wanna get. So let's see, Etsy host, for example. They can download this locally here on their local machine, or they can SSH into this VM and SCP the file over to a target location. So what I'd like to show you next is a use case example of a tenant level migration within a cloud. Now, this example can be used from a tenant in one cloud to a tenant in a new cloud as well. All right, so you'll see here that there's two workloads, a demo workload and a summit VM workload. And within those workloads, you'll see some VMs here. All right, and for one of these migrations, there's a couple of bits of information that you'll need. So what you'll need to do is identify the tenant ID of where you wanna migrate your workloads from and where you wanna migrate them to. So what you'll see here is, you'll go to the projects here, identify the tenant ID of where the workload is now and where you want the workloads to be migrated to. And then you'll identify the user that has the right privileges to be able to execute this type of command. This will typically be done by a cloud admin that has access to the ChilliAvult VM or the workload manager here. So what you can do here is list out and double check all the workloads that you want to migrate. And what you'll see here is that the IDs, the two workloads that you wanna migrate and the IDs that are associated with them, the project IDs. And then what you'll do here is specify with a workload reassign command the old tenant ID, which is the current one, and then a new tenant ID that you wanna migrate the data to. So we'll see here this command here. So old tenant will be the current tenant. So copy and paste that over here. And then we'll assign it to a new tenant, which again, this can be within the same cloud or a new cloud. So we'll get the ID of that new cloud here or a new ID here, paste that in. And then we'll get the user ID that has the right privileges to be able to do this. Now, once this command has been executed, what you'll see from a result is the workloads and their new project IDs that they're assigned to. And then we'll log into the cloud as that tenant or get into that tenant. And we'll see here that from an instance perspective, Summit DR is the new tenant. From an instance, there are no instances. There are no volumes. And then we'll go to the backups tab here and see that the workloads are now present in this tenant workspace. And from a recovery perspective, what you'll do is identify a point in time that you wanna restore into this new tenant workspace. We'll run a selective restore. We'll name the restore from auditing purposes what was this done for and why. This is where we can choose a network mapping on this new cloud or the current cloud I wanna bring these VMs up onto this new network, change the volume type mapping if this new cloud has that. And then at a per VM level, we can choose if we wanna change the name or anything like that. And then we'll hit restore here. So this will take from the target repository and restore this back into the new tenant workspace. So once that is completed, just see here it's executing and available, what you can go back now and see is that the tenant workspace, if you go to your instances, you'll see that now there are five VMs from that workload have been restored into this new tenant workspace. And then the volumes that are all associated with them as well. And then again, if you'd like to, you can go in to do the other workload to get the full tenants workloads migrated over, same type of operation. You do the selective restore. Change the network mapping if you want. Change the volume type mapping. And then again, from a each VM perspective, if you don't wanna include one of these VMs, you can uncheck it, exclude it, and then hit the restore button. And this will show the last few VMs as well, being migrated over. Once that's available, double check the instances. Now that the other three VMs are there. So all eight VMs from that tenant's workspaces are now in this new tenant. And then the volumes that are attached as well. And then if you'd like from that point forward, if this is going from a pre-prod to a production environment, you can actually enable protection against those VMs again by just going into the workload, editing those, and turning the scheduler back on. And that's it. I mean, it's as easy as it looks and it's even more powerful than it is as well. So appreciate the time. Thank you very much.