Welcome back to the workflow clip. Today's part two of how to be a fast animator. And today it's the Maya edition. Why Maya edition? Just because part one over there was about preparing in a way and things you should do almost before you animate, while you animate afterwards. It's more kind of a workflow habit type of thing. So like planning, taking proper notes, stuff like that. But that is also like if someone asks, I want to get six pack abs. How do I do this? And they expect some specific exercise and that's it. And they can keep eating pizza and look at myself. And the answer is always, well, you got to change your nutrition. And that's kind of the way I'm approaching this now. It's like, well, I don't want to do all the planning. I don't want to test my rig. I don't want to shoot reference. I just want to animate and be fast while I animate. So, OK, well, today is all about that. I'm still assuming that you looked at part one and you did all those things like testing your rigs and planning and putting reference and all that stuff. Really important. Well, let's pretend you did all of this and you are actually animating in Maya. You got the scene open. What can you do to make the workflow faster? And today I'm going to talk about types that are immediately applicable within Maya. I'm not going to talk about tools just yet, just because you might not have those tools. You might be at a company where they don't have those tools. You can't afford those tools. What can you do within just the Maya that you have? Of course, there are versions and there are different things about that. But you open Maya. What can you do to speed up your scene so that you can animate faster? That's what we're going to do today. So let's check it out. So let's check out the scene. This is what I'm currently animating and it's actually a great example of also what not to do. It's a very messy scene.  I have nothing referenced. There's a bunch of stuff I should change, but that is, I can't wait to talk about this scene and from beginning to the end, like what I did and what I did wrong and all that good stuff. But here's what I have here. This is the full scene. You can see how it's a bit stuttery. It's not super smooth. That's potentially what you have in your shop. So let's pretend I actually have a reference. So I brought in a reference. This is my stormtrooper here and you can see there's a massive, massive scale difference. Again, something that affected my renders. Oh, so what I want to talk about.  It's that little blue extra thing that shows you it is referenced. So if you have one character and that's what you want to focus on, let's pretend you have a bunch of other characters within your scene. Take your characters and unreference them. You can do a right click, reference, unload file and gone. You can also go file, reference editor and in here you can turn it on and off. As you can see, I got no references in here. So that'll be tip number one. On reference, everything you don't need. So right now I only have this scene and this is what I need. That being said, I can play this. Again, it's not super smooth. I can go through my camera. And one thing you'll see, the character is fairly smooth looking just because if I select the character here and I hit one, now he's rougher. So this is three, this is one. So now you can see the scene is already faster. Now I'm screen grabbing this. You're not gonna have the exact thing that I'm seeing right now, but it is for me, I can already tell it's faster. Something else that will help you too is to actually get rid of more. I have the set here. You can see that table is breaking. If I go into this other view, there's a bunch of stuff in my scene that's gonna make the whole thing slower. So I would hide this, hide that, hide this, hide everything you don't need. I have this all grouped so I can hide all of this. And again, you look at what's going on right now. It's already faster. Now if you're hiding everything, obviously you can see that there is no ground. So foot contact might be a problem if that's something you need to take care of in your scene. So just create, poly, plane, and I can extend this and bring it down. Of course you make it work so that it's actually where the feet are touching, but that just creates some temporary set pieces in case you have a wall, stairs, a ground or something that gives you a rough representation of what you need to see. Again, I have R2 in here, take him, and this is part of my problem with the render thing. I had to duplicate everything. It's a long story. But I can take R2, hide everything. I have Grogu in the back, let's hide him, and I have BBA, let's hide him as well. What else is left? I got a bunch of stuff. Oh, I got some fake liquid here in the back. But again, you play this, it's getting better and better. Something else that will help is actually to unload your audio. So I'm gonna go, you can't see it here, but I'm gonna say audio off. And a lot of times that helps me as well. Again, as you're watching this, potentially you might not see it because of the screen grab and YouTube upload. But trust me, I can scrub through this and it is buttery smooth. Now, sometimes you might have a scene that you do wanna see things where you add the lights.  Don't do that. If I play this now, oof. All right. It's not too bad, but I feel that it's definitely slower than before. Sometimes you wanna see it.  But when you animate, don't do this. Now this rig is very fast. I can probably still use it, but generally I don't have it and I turn all of those things off. Textures sometimes depends on what you wanna see. But if again, if that doesn't work and textures are no good, turn them off. Now if you have other things in the scene, and I'm just gonna turn everything back on so you can see what's going on here, you can also go into shading, use default material, and that will take everything off. So now again, I can scrub and you can see it is buttery smooth. It's really nice. Now let's pretend you have multiple characters and you just wanna concentrate on one, but you still wanna see what else is there. What you could do is grab your character. So if the whole character is selected, you go to cache, alembic, cache, expert selection to alembic. And then you can name this. Don Alembic, it's very boring. Say yes. And it's gonna go through the whole scene one by one here for my frame. This is gonna be very slow-ish. So I'm gonna pause the recording. Okay, on pause, it's finished. So let's say I'm gonna hide this character. I'm actually gonna hide even more. Now just have the lights here in there. Whoop, PBA. You play this again, this is very, very smooth. So what you can do now, you can go file, import, navigate to your alembic directory, grab this guy, open, and there he is. Now you can see it also took all of this. So obviously you can go in there and hide all of that. And of course, my expert selection was very basic. You can tweak the settings when you export. But there you go. I got the character in there. So if you have this with other characters, that will definitely be of help. So you can still see what's going on with other characters you animated. So you have one main character with controllers. The other ones are there for reference. This character also has the display mode and display type. So you have a template, you got a reference here that's in terms of selection. You have modes here with proxy. So when you move this around, it is really fast. It's even faster. This is just blazingly fast. So if you have a rig that has that option, of course that's awesome. You can switch it to normal, texture, and render. So again, if you have a proxy model that's cut up like this, absolutely fantastic. This is gonna be so fast. Now let's pretend you don't have this. What you can do is just create a sphere, take your head controller, take this, point, and orient, take your sphere, make it a bit bigger, move it where it needs to be. Of course, now I messed up my constraints, but that's okay. And I can also use a parent constraint if I want. So if you take all of this, pretend I'm gonna hide the geometry here that's on my layer. Hide this, you of course name it. But you can put a sphere for your head, a sphere for the chest, for the hips. Now when you play, it's still super fast. So if your rig doesn't have a proxy mode, absolutely put in the helper object and you can put this for head, chest, and maybe cubes for the feet.  But that is absolutely a way to go. And usually when I animate, not usually, but sometimes I grab my sphere and I just animate a bouncing ball in a way. I move the sphere around pretending that this is the character to kind of map out the timing. And when I'm done, I bring it back and I put the rig in and then I take the rig and match the position of what the sphere was doing. This could be just the heads, the root, whatever it is. But again, super fast. Go through the camera view and it gives you a good indication of where your character is going to be. Now, something else that I have that is also built in my are hotkeys. So if I have something like this, I don't wanna go into here, go here, and then, well, I wanna grab, let's say, this guy.  That used to be a special script, but now if you go into Windows, Settings, Hotkey Editor, you have a bunch of stuff already set up where you don't have to do any custom scripts. I still have some of these for the channel keys. The Outliner collapsing, so let's pretend I have a bunch of stuff open. Let's open all of this here. Maybe you don't wanna just go through all of these separately with my hotkey, so let's reopen all of this here. I can just hit O and it collapses everything. Stuff like that is really helpful. Actually, I don't even know what that is. That is not something that I usually have. I don't know what that is. So I have hotkeys to frame forward and back for NURBS on, off. Undo for me is U and not CTRL Z. Everything is within my reach when I put my hand on the keyboard. Also, I'm left-handed, I don't know if that makes a difference, but that's what I have here, as well as zooming in. So I can hit my hotkey and it goes into my zoom mode. That's the 2D pan and zoom. And then under hotkey activates it here and now it can go around the scene like this and also switch over to zoom and zoom in and out. And that is super useful. I would definitely use the 2D zoom and pan tool. Now that being said, if you have a newer version of Maya, you can also go into your preferences and there's cash playback. I have it off just because my scene is already so light, I don't need it. It's also really long. So when you put on cash playback, I have found that sometimes you have a really long scene, it takes a while. It just takes a long time to work this out. Also, when you have really slower rigs, it takes even longer to create that cash playback setup here. But again, tons of options. So customize this if you have a version. I'm in 22 right now, Maya 2022. I also use Maya 20 as well. So whatever Maya you use, you might have or might not have certain options. Now there's a lot more you can do. I love the Zoo Tools Pro. There's a bunch of stuff in here that's super helpful in terms of just shaders, lights, camera setup, especially randomized keyframes. And I use this a bunch for handheld camera stuff. But again, tools will be in a part three or four or in the future, there's a lot to cover. And no, I don't have animbot just yet and I'm gonna use that as well. So a lot of tools that I'm gonna cover including Zoo Tools Pro and other ones that I have. There we go. These are the tips. Hopefully it will help you to be combining that plus part one with your preparation and your planning and all of that. Plus future parts when you talk about specific tools where you can animate even faster. But it's more of a different workflow thing. I think you're gonna be on the right track to be really fast. And as always, if you have other tips, other things you want me to add, comments are open, you can always let me know. And stay tuned for the future parts. It's gonna be things like you don't want to use all the controllers and all the blame chips, whatever you have when you rig, just use the smallest minimal amount of controllers to get the maximum amount of information on the screen. So it's stuff like that about workflows and tools. There's more to talk about. There are many ways how you can be a faster animator. Now, speaking about fast, if you feel like I want to be fast and faster even with my help, I don't know if that works as a segue, but I do have workshops so you can sign up at any time. You can show me your shots, I can help you. Let's talk about those things that can make your shots even more awesome. So sign up. So check it out, I'll link in the description with all the information. You can sign up at any time. You can start whenever you want. It's all very flexible. And speaking of time, if you're still watching, thank you so much for watching. And since I didn't pitch anything at the beginning, here's my pitch at the end. If you like all those things, you can hit that like button. You can also subscribe if you want to hit that bell button so you don't miss any of my uploads and helps my channel grow. And hopefully you get notified about other future, hopefully awesome clips. And that's it. That's it for the pitch to pitch your pitch at the end. And that's that. Thank you for watching and I'll see you in my next upload.


Moving on, gunmen suspected to be bandit have again kidnapped no fewer than 13 persons in Nguan, Guimbi, a community of sub-boying Chikun local government area of Kaduna state. The incident happened late Wednesday night. Now, sources say that about 6 persons, including a landlord and his tenants were kidnapped from one house. This attack comes barely 48 hours after the abduction of Bessel Baptist High School students in Maraban Rideau in Chikun local government area on Monday morning. Now, youth in their large numbers have taken to the major roads of sub-Ontasia, Kachia area of the state in practice over the attack on Thursday morning.


Hello, welcome to SSMunitech, so see in this side and in this video we are going to see about the linked or shared self hosted IR. So in the last videos we have seen about the what is integration runtime and what are the types of the integration runtime and we have also seen about the self hosted IR in detail and in the last video we have done the demo under the self hosted IR and in this video we are going to check about the linked or shared self hosted IR. So if you haven't watched the previous videos so I would strongly recommend to watch that video for better understanding. So go to the next slide and here what is the linked self hosted IR. So we can use the existing self hosted IR that we have created already that exist in another resources. So for example we are having a data factory and where we have created the self hosted IR and we want to utilize that self hosted IR in the second. So we are having that option by using the linked self hosted IR. So this way we can reuse the existing infrastructure where self hosted integral runtime is set up. So what it mean? So basically if we are going to create the another self hosted IR for the ADF 2 so it will be replicating the same thing. If we have already created in one place and we can use that in another place so that is the good one. So this option is already available inside the self hosted IR ADF. So go to on the browser and we will see in the practical. So here if you have remembered in the last video we have created the self hosted IR the last one. So this is we are under SSU training data factory. So we want to use this self hosted IR in another data factory. So here we can see the data factories and let me try to open this SSU test. So under this data factory we want to create this self hosted IR and will be utilizing that we have already created under the SSU training. So here let me click on this open. Now we can go in the manage and under the manage here we can go inside the IR that is integration on time. So by default we can see we are having this auto resolve IR that is available at the runtime when we are creating the SSU test. So we want to create a new IR here but before creating the new IR we can go in the existing one. So here let me click on this. So now here we can see the option for the setting and the node and the auto update. So let me quickly cover all these. So under the node we have already discussed about the nodes if we are going to set the multiple nodes then that will be high availability option and in case of the auto update so we can set the time by which this will be automatically updating. So we can enable and we can disable from here for the auto update option. Let me go in the sharing. So under the sharing we can see here we are having the resource ID. So we have this value and below this we can see the grant permission to another data factory. So here we have to provide the permission first for this SSU test. Once we will provide the permission and after that we can create the integration runtime under the SSU test with the same one. So first let me click on this and here it will take little bit time. So here we can see all these data factories. So let me call this with the SSU test. So this one so we can click and we can add. So it will be going to add there. So now we can see it is loading here and added this data factory. So we have provided the permissions here. Now we can copy this resource ID this value. So let me try to copy and I'll show you where we can use this. Next option we can see the link. So under the links we don't have anything right now. Let me click on apply. So once we have done with this change then we can go in the SSU test and here we can see the integration runtime setup. So we can select the first one that is as your self hosted click on continue. The last option here can see linked self hosted. So instead of selecting this self hosted it will be going to create the self hosted IR but if we are going to get this self hosted from the external resource it means from another data factory. So we have to use this linked self hosted click on continue. Here we can call this as linked self hosted IR. So it is validating that is good. Here we can see the type that is self hosted that is linked and here we have to provide the same resource ID that we have already copied from the existing one. Now we can click on create. So it will be creating the new IR. So it is failed to error occurred. Let me try it again. So this time we can see we are able to save this. So let me click on that. So this is our resource ID and this is the name under the node we can set the nodes and here we can see the auto update option and we don't see the other options because we are only inheriting this IR from another data factory. So go back to here and here let me open this and this is the IR actually. So here we can see all these options but we are using this under this so we cannot see more options. Now in this data factory we can create the new pipeline and under that pipeline we can directly load the data from your SQL Server table that we have already done in the previous video. So for the testing purpose you can do the same. If you will be facing any issue then you can comment in the comment box. I will try to response there. So thank you so much for watching this video. If you really like this video please subscribe our channel to get many more videos. Don't forget to press the bell icon to get the notification of our newly uploaded videos. See you in the next video.


What is the centripetal acceleration of a person standing at 28 degrees north of the equator? This is similar to the last problem, so a similar approach should work. The centripetal acceleration and speed are still given by the same equations, and the period will still be 24 hours for the person standing at 28 degrees north of the equator. However, the radius of the path that the person follows will be different. If we sketch out the path the person follows alongside the radius of the earth, we can see that the person will follow a different circular path with a different radius than they did in the problem before. To work out the length of the radius r, we can use trigonometry. The radius of the path and the axis of the earth's rotation meet a bright angle. If we label this angle theta, we can see that the angle theta is 90 minus 28 degrees, since the angle between the radius of the equator and the axis of the earth is 90 degrees in total. So now that we have an angle, the radius of the earth, and a right angle triangle, we can use trigonometry to work out r. From Sokotoa, we know that sine of theta will equal r. The radius of the uniform circular motion path of a person standing at 28 degrees north of the equator divided by r-earth, the radius of the uniform circular motion of the person standing at the equator, which is equal to the radius of the earth. Rearranging, we find that r is equal to r-earth times sine theta. So now we have everything we need. As before, we can substitute our equation for velocity into our equation for centripetal acceleration. Big T is still 24 hours of 3,600 seconds, and r is the radius of the earth times sine of theta, or 6.4 million meters times sine theta. Now, we could plug these numbers into a calculator to get our answer, or we could make life a bit easier by recognizing that all of these terms are the same as in our previous example, except for our sine theta. And all of these terms will multiply together to give us 0.034 meters per second squared. So we can then see that the centripetal acceleration in this example is 0.034 meters per second squared times sine of 90 minus 28 degrees. Calculating, we find that this comes to 0.030 meters per second squared. Does this look right? Let's check. If our person was back on the equator, theta would be 90 degrees minus 0 degrees, which is equal to 90 degrees, and sine of 90 degrees is 1. So we'd get the same answer as we got previously. That's a good sign.


Is there a right way or wrong way to have an altar call? Or are they just all together unbiblical? Hey smart Christians, if you notice on YouTube or different places that you go, you'll hear people have a varying opinion on altar calls, whether we should have them or not, the dangers and so forth. Now, there are some people who I absolutely respect, who I absolutely also disagree on this topic. One such person is Paul Washer. Overall, I love his doctrine. I like what he stands for and how he presents himself. But this is one of these things that I'm going to have to disagree with him on. Let me share it with you a clip from him and then I'll tell you where and why I disagree. So you draw the net and you get people to come forward. Now look at how they do this. The music plays a, it must play a role because I noticed that they always make sure they got the right music. So that must play a role. Some even give the lights, lighting's important. Okay, and then also you got a big enough congregation. You don't put your counselors down front. You put them in distinct places all around the auditorium so that when you give the invitation, they come forward, which makes it easier for the other people to come forward. Tell me that doesn't happen. It does everywhere. That's a cult. That's manipulation. And I'm going to call it exactly what it is. It's wrong, it's sin, it's deceptive. And it has nothing to do with the ministry of Jesus Christ or the apostles. Now if a man is up there preaching and he wants people to come forward, he's preached the gospel of Jesus Christ. He's crying out for men to repent and believe, to take their stand no matter the cost.  And people come forward and they're broken and he counsels them for four hours. I got no problem with that. That's not what's going on. They come forward and then someone who has been supposedly trained in counseling. Now notice, trained in counseling but not trained in the gospel. Comes down to the clipboard, talks to that person for five minutes, gets that person to pray a prayer. And then preacher comes by, reads the Bible and pray. Come on up here. Brothers and sisters, I want to present to you your new brother and sister in Christ. That is so wrong. There's a reason why God gives elders or pastors to a church. They are men who supposedly qualify for the ministry. I'm not going to have just someone who's trained in how to get someone to pray a prayer. If someone is seeking the Lord, shouldn't an elder be there? Shouldn't someone trained in theology and the discernment of souls of the working of the gospel in the heart of a man be able to sit there and maybe sit there four hours and maybe have to meet with them for the next six weeks before anything like, we'd like to present to you someone who has made a professional faith. Isn't it wonderful when you go to a church and someone says a few weeks ago, so-and-so came, shared the gospel with him, we spent time with him in prayer, discipleship, gone through many things like the Book of First John that this person might have biblical repentance. We considered their conversion experience and everything else. They have the assurance and we as pastors ourselves have scrutinized this situation. We prayed over and with great assurance and great joy we present to you this brother to be baptized.  As a matter of fact, there are people that are going to abuse everything. There are going to be people that are going to abuse the offering. There are people that are going to abuse the praise and worship service.  This is just how it is. As long as you've got fallen men in this world, you're going to have some of those fallen men coming to the body inside the church who will also act inappropriately or who will exhibit behavior that they should not do. You'll even have churches where they're intentionally trying to be deceptive or they just honestly make a mistake. You're going to have some errors in people coming to the Lord. I believe though that an altar call can be good. I don't think the church has to necessarily have an altar call. However, here is the good thing about an altar call and then I'll deal with some of the bad things that he stated. An altar call allows the church, the body to know if a person has stated or professed faith in Christ. Obviously, when someone professes faith in Christ, when they say so, we don't know if they genuinely have or not. That's always the case. But what it does is it gives the church the opportunity to do the next thing that's just as important as them coming up and saying that they have placed their faith in Christ and that is discipling them. Jesus says to go and make disciples. Well, it'd be kind of hard to make a disciple of someone who you don't know is actually a believer. Maybe they attend church, but we don't know where they stand with Christ. But at least in this regard, whether they are telling the truth or not whether they have genuinely placed faith or not, it gives the body an opportunity to work with this person that the person pushes away and decides not to. They don't want to be a disciple. They don't want to attend me. They don't want to grow. The Bible says, well, then that might be an indication because this person did not actually place faith in Christ. But what you have is a point of contact. You've got an appointment reference. You can say, okay, well, let's, Joe Bob here has stated he places faith in Christ, but we don't see any fruits of it. And so we can design something around reaching to Joe Bob. Maybe there's other things in his life. Or if he actually did profess faith because one thing we discount sometimes is that they may have actually genuinely profess and place their faith in Christ. And so then what do we do? You don't want a new believer standing in the body, helpless, not knowing what to do. He or she is just coming to church, clapping and praising and then goes home throughout the week and is left to fend for themselves. But the other thing is that altar calls are not just for people to place their faith in Christ. They also are there for prayer. Let me go to James chapter five. And I want to read this passage. This is, I think, the biblical way to have an altar call. James says in five verse 13, he says, is anyone among you suffering? Well, quick question then. How do we know if you're suffering if you don't tell anyone? Now churches can have it to where they have an altar call or they might fill out a slip of paper or they may have someone stationed where they can go and speak to that person but there needs to be some sort of way whereby the people can go and seek the elders to let them know, hey, I've got some sort of issue. Typically an easy way to do so historically in the American church has been through an altar call. Let's go back to it. He says, let him pray. Is anyone cheerful? Let him sing praise. Is anyone among you sick? Let him call for the elders of the church and let them pray over him, anointing him with oil in the name of the Lord.  And if he has committed sin, he will be forgiven. And so these are things that could be fleshed out at an altar call. Granted, there are some bad things that can happen at an altar call, but there are some good things and this is what could take place. Let's continue. Therefore, look what he says, confess your sins to one another and pray for one another that you may be healed. The prayer of a righteous person has great power as it is working. Now, it doesn't necessarily mean that you should confess your sins to the entire body. Hey, everybody, I've been smoking. I've been watching things I shouldn't watch. I've been going places I shouldn't know.  And then what happens? Well, if you've acknowledged that point, well, then the next step will be then, since I'm a sinner and I know I need saving for my sin, well, who do I turn to? I'm in a church. Can you all give me any sort of clues as to who I should turn to if I've been sinning? Oh, Jesus, I've heard the story. What must I do to be saved? Believe on him and the Bible says you shall be saved. Well, those are the kind of things that can happen at an altar call. Let's continue. I wanna show you something. Let's drop down to verse 19. My brothers, if anyone among you wanders from the truth and someone brings him back, let him know that whoever brings back a sinner from his wandering will save his soul from death and will cover a multitude of sin. So let's say, kind of like what Paul was saying, Paul Warsh was saying, if there's someone there who just comes to his altar call and they're not really genuine. They're not true believers. They may have stated something, profess something, but they weren't really true. Well, the Bible says that anyone among you, us believers who wanders from that truth, that person also, who he heard the truth, he knows about it, but he wanders away from it. Then look what he says. He says, then let him know that whoever brings back a sinner, that person who made a profession of faith and weren't genuine or they're thinking about it, but whoever brings him back, bring this sinner back from his wandering will save his soul from death and will cover a multitude of sin. And so that's one regard where, for one example of how having an altar call, if it's done properly, could be beneficial. Now, are there some altar calls that are just for show? Sure. There are, I've seen churches where they've had plants where people get up, they've got folks who just get up and come down to the altar who are supposed to be believers already. That's debatable, but who will get up just to kind of lead other folks and things. And yeah, it's okay to get up and go also. So I won't be the only one, or people that, you know, they've got the lights, the music and so forth. Now, are the lights and the music in and of themselves a bad thing? No, it's not for show, but okay, I don't have a problem with it if it's done and it's done kind of tastefully and not too obtrusive, not too attention grabbing and so forth. I really don't have a problem with that. The main thing is the main thing and that is getting people to actually, genuinely place their faith in Christ. Well, that's done not just at that moment that makes the decision. It's done before that as they're understanding the gospel. Are there, what I do like Paul Washer says, is it always necessary that you've got to spend four hours with the person for a day or weeks and so forth? Well, if that's necessary, I will give everyone this bit of advice. Remember, we're always in sales, even just our regular life. If we get pulled over by the police officer and we want the ticket, we're in sales. We go before the judge, we're in sales. If you are maybe dating some young lady or some man or what have you, you're in sales. If you want to buy a house, if you want to sell something, you are in sales because you're trying to get your point across the way you see it. Well, if we are trying to share the gospel, we are in sales. And the key is this, take exactly the amount of time that's necessary. Meaning this, if it takes five minutes, you don't have to give them 20 minutes. If it takes 20 minutes, then don't take five minutes. So there are gonna be people who need to stay there and you're gonna have to labor with them for hours, for days, for weeks, for months. That's gonna happen. But then there are some that make the profession at an altar call at that moment because they have been hearing the gospel. There are people who have heard the gospel and they got it. Once the Holy Spirit has regenerated their heart, guess what they don't need? They don't need you to keep saying to make sure do you really, really, really, really, really have it? Because what it becomes in, it becomes more my preference. And we don't want someone's preference to take the place of what God's standard is. I get it that certain churches do things a different way. That's fine. But if there's no such thing as a perfect altar call or perfect way that the church does things. Because remember, the Bible does not tell us the exact order of service that we ought to have. Now I would always caution anyone to be careful with altar calls with everything. Again, any and everything in the church could be abused as a matter of fact has been abused. But are altar calls inherently unbiblical or wrong? No, they can be a useful tool. I've seen them used as a useful tool. I've had altar calls. I've stated this way and I've given people the opportunity. If you want to come down for prayer, if you feel like you need prayer, if you want to make a profession of faith publicly or if you don't have to, you can do so. You don't, coming down to the front does not get you into heaven. That your profession of faith, your actual faith in Christ is what does it. So if you want to do it right in your pew, hey man. But we would ask that you would let someone know of your decision if that's what you've done or if you have a prayer request and you don't want to become, if you don't want to get up before the people, then fill out the form or see one of the elders before you leave. So there's different ways to go about doing it. Again, the altar call is the most visible way to go about doing it, but there is no right or wrong way. And now I'm not totally against what Paul Washer is saying, but there are some things where I disagree with it. So I'm not against an altar call. I think that if it's a tool that can be used, well then let's use it. Hey man.


So as hopefully you've noticed, I am not Andy. Andy and KB welcomed their first child last week. Everyone's doing well. And we're extremely thrilled to see the Andy Pavlo parenting experiment. So the clicker stopped working. So last class, Andy talked a lot about acid properties of transactions, focusing a lot on isolation in particular. Reasoning about whether schedules are serializable, and we talked about conflict serializability and view serializability. And with conflict serializability, we were able to verify whether a schedule was serializable based on being able to swap operations around or looking at the dependency graph and being able to say, yes, this schedule is serializable or whether it would violate the serializability guarantee. And then there was this other notion of view serializability, which most applications are actually no systems really support, because it's this concept of being able to produce results to the application that look right to the application, but actually aren't serializable as we've defined them. Or it wouldn't be conflict serializable. So I think he gave a couple examples. One would be like counting all the number of accounts that had a positive balance while he was shuffling money around between the accounts. And because the accounts never dropped negative, we got the correct result. But it wouldn't technically be a conflict serializable schedule. I think another one was just doing blind rights into accounts. And because the last right that we cared about was the one that got executed, it counts as view serializable, but it's not actually conflict serializable. So there was a lot of time spent on like, how do you verify these schedules? But what now we want to care about is how do you do this stuff in real time? Because he's got an example here. So an example of this is not conflict serializable because we have this right on A and then another read on A. Unrepeatable read, this is not conflict serializable. So that's basically what Andy talked about in the last lecture. I'm going to try to make up for time here because we're starting a little late. So as I was saying, we need a way to do this on the fly in a real system. We don't get the full schedules of transactions when they're submitted. When clients connect to the database and they start a transaction, we don't know what the queries are going to be a priori. So we can't look at what the client's going to do and predict whether their operations are going to be serializable or not. So we need a protocol, we need a system that's going to allow us to execute these transactions in parallel because we want parallelism in the system, but we still need to guarantee isolation between these transactions. So I think Andy briefly mentioned this idea of there are pessimistic approaches and optimistic approaches. Today we're going to look at two-phase locking. We're going to use locks in the system. This is a pessimistic approach because basically you're going to have to ask for permission from the system before every operation. So before you can do a read or a write on a tuple, you're going to have to ask for a lock on that object. We'll get to optimistic stuff in the next couple of lectures, but today it's all about pessimism with locks. So this is just a high level what I was just describing. An example of before we can do a read on a, we're going to go to a lock manager and say, I want a lock on a. This lock manager is sort of your central authority on whether you're allowed to access a tuple or not. It maintains metadata about who holds locks and enforces the lock protocol in the system. So T1 is going to begin. It's going to request a lock from the lock manager on a. It gets the lock as no one else holds it. T2 begins, let's imagine we're in a single-threaded environment, so we're only executing one of these transactions or we're only making progress on one of these transactions at a time. So T1 begins, gets the lock on a. We switch to T2, it begins. It wants the lock on a because it's eventually going to do a read and a write down here. And it's denied by the lock manager because T1 already holds that lock. The only thing T2 can do here is stall. What the behavior is is kind of up to the implementation. But there's going to have to be some sort of stall because you can't get the lock that you want. T1 proceeds, does its right, does its read, releases its lock. The lock manager is now going to basically tell T2, you can get the lock that you want on a. It's going to grant it. T2 is going to do what it needs to do. And then it's going to release the lock. So conceptually, that's how we're going to use locking in the system. There's a little bit more nuance to it than that. And we'll go through that through the rest of the lecture. Makes sense? We're good? Cool. So today we're going to look at the lock types first. We're going to start with two basic lock types. And then we're going to get into our first proper concurrency control protocol, two phase locking. We're going to talk about how we reason about deadlocks and how we're going to solve that problem. And then we're going to introduce the notion of hierarchical locking, which is going to allow us to take fewer locks in the system to be a little bit more efficient about how we use locks. And then the last thing that actually is a mistake. We're not going to get to isolation levels today. He didn't make it last year. And they're not in these slides anymore. So we'll get to isolation levels. I think we'll handle them in the next lecture. So I think you've seen this slide before. Andy presented this. This is from work by Goetz Greifah. He showed it when you were talking about protecting concurrent data structures with latches. So I think concurrent B plus tree indexes, hash indexes, things like that. And I'm pretty sure this slide exists just to get Dave Anderson to stop making fun of us because he says database people are weird for calling locks latches. But they are distinct terms in our world. Before Andy was talking about, like I said, latches to protect threads from trashing the memory of your concurrent data structures, today we're talking about locks, which are protecting logical constructs in the database. So things like databases, tables, tuples, they're designed to protect transactions from conflicting with each other. And probably the key distinguishing part is that with latches, we avoided deadlocks with programming discipline. We were writing the logic on how these latches were acquired with things like latch-crabbing in the B plus tree, how we could reason about what the operations were doing. So we knew how to safely acquire latches in one order and then make sure we respected that order and release them in the correct order as well to avoid deadlocks. With locks, we can't do that because we don't know what the transactions are going to do, what queries they're going to run. So we need to be able to handle the clients issuing queries to the system that would result in a deadlock and we need to talk about what are we going to do then. Because it's pretty simple to, as we'll show in a demo a little bit later in the lecture, it's pretty easy to put the system into a deadlock. So like I said, we'll start with two basic lock types. We have shared locks and exclusive locks. Shared locks are used for reads. Exclusive locks are used for writes. These are kind of similar to the read-write latches, I think, that were talked about in the concurrent data structures. The compatibility matrix kind of represents, if you already have a shared latch, can you hand out more shared latches to that? Yes. Exclusive latches are more or less incompatible with any other latch types. You can't hand out multiple exclusive latches. You can't hand out an exclusive latch to something that already has a shared latch. And that's the basic compatibility of these locks. I shouldn't call them latches, because Andy will get mad at me. So the basic semantics of how we're going to work with locks is, as I was describing before with that example, you're going to go to a lock manager and you're going to say, hey, can I get a lock on this object? The lock manager is going to decide, based on its internal metadata, whether you're allowed to have that lock or not. And then it's up to the transaction to release the lock when it's done. The transaction managers, or excuse me, the lock manager is not necessarily putting any sort of constraints on how long necessarily you have that lock for. So it's up to the transaction to make sure they come back, release the locks when they're done with it. And then it updates its internal metadata. I don't think there's a, Andy's not doing a lock manager project this semester. We've done one in the past. But the basic implementation is there's an internal table, probably a hash table, that keeps track of all the possible locks that are taken in the system. And then as requests come in, you put them into a queue, you process them off the queue, and you go forward from there. So it's a cool project, but I don't think you're doing one this year. So let's look at an example with these shared and exclusive locks that we defined before. So T1 begins, it wants to do both a read and a write on A. So it's going to go straight for the exclusive lock. So it's going to go to the lock manager, give me the exclusive lock, lock manager's like, sure, no one else has a lock on A. It's all yours. T1 completes the operations it needs to do on tuple A, releases the lock because it's done. T2 comes along and says, I want to do a write on A. So it wants the exclusive lock now. Lock manager's like, sure, no one's got that lock. It's all yours. Do whatever you want. If it does its right, it unlocks A. And then T1 wants to come back and just do a read, so we only need a shared lock. Once again, ask the lock manager, give me the lock, does its operation, releases the lock. So as we described in the last slide, transactions are responsible for locking and unlocking. The lock manager's just kind of here to kind of direct traffic and make sure everyone's doing what they're supposed to be doing. Does anything jump out as a problem with this schedule? Right, it's not isolated. So in particular, when T1 at the end does another read, it's getting an unrepeatable read. It read tuple A, it did a write on A. But then it goes back and it reads A. And it's actually seeing the value that T2 wrote. So we have an unrepeatable read anomaly. We can't have that. So we're going to need to be a little bit smarter with what we do with our locks. We're going to need a little bit more discipline and a protocol applied to what we're doing here. Right, so Andy added one more cool animation to show, this is a problem, here's your unrepeatable read. So what we're going to use is a protocol called two-phase locking. And it's going to allow the database system to hand out locks in a manner that always guarantees conflict serializable schedules. This is a big deal because now we don't have to try to limit the parallelism in the system. We can try to run multiple transactions at the same time. And we don't need to know what the transactions are going to do ahead of time to be able to decide if they're conflict serializable. The system can run, we can do operations, and we can hopefully get high throughput on transactions without generating un-serializable schedules or violating our isolation requirements. This is all work that came out of IBM in mid-70s for the System R project. It was led by Jim Gray, who later went on to win a Turing Award. So this is pretty groundbreaking stuff. At the time, they were building one of the first relational database management systems. So there was no textbook on how to do this stuff. Two-phase locking is what they came up with. Turns out it's a pretty good idea. Most systems use two-phase locking now. So you're probably not surprised to see that two-phase locking has two phases. There's the growing phase, and then there's the shrinking phase. So in the growing phase of a transaction, it's allowed to acquire whatever locks that it needs. It goes to the lock manager, gets everything that it needs, and then it performs all of its operations. And then the second it releases a lock, it tells the lock manager, I'm done with this lock, the transaction is now in a shrinking phase. It is no longer allowed to acquire any more locks. So in an example, we'll see how this helps us out, how this prevents the anomalies we're concerned about. And I think, yeah. So another way to think about it is this visualization. x-axis is time. This is the lifetime of a single transaction. y-axis is the number of locks held by that transaction. So in the growing phase, it can continue to accumulate as many locks as it needs. The second it releases a lock, it's going to go into the shrinking phase. You get no more locks. So this is an example of violation of two-phase locking. Because the transaction released some locks, and it went back and acquired some more. We can't have that. It would cause the same sort of problems we saw in the previous example, where we had an unrepeatable read, where you released a lock, and then went back, acquired a lock, and you get anomalies because of that. So here's an example with two-phase locking. Transaction one is going to begin like before. It needs an exclusive lock, because it wants to do both a read and a write on the tuple. It could start with a shared lock and upgrade it. There was a notion on an earlier slide of upgrading. We'll talk about upgrading a little bit more later in the lecture, but we're going to start for now with an exclusive lock. Goes to the lock manager, gets the lock. T2 wants the exclusive lock. It can't have it, because T1 still holds it. So we're going to keep holding onto that lock until we're done with all of our operations. We're going to do the extra read that we wanted in T1. Then we're going to release the lock. T2 has been stalled this whole time, not able to make any progress, because it didn't get the lock on A. Lock manager's going to say, OK, T2, you get what you want. Do your write. And then T2 is going to unlock and then commit. So the great thing about two-phase locking is, if you follow this protocol, you will get conflict serializable schedules. The dependency graphs are guaranteed to be acyclic. This is great. This is exactly what we want. It does have a slight problem, though, and that is cascading abort. So we'll give an example on the next slide. But the idea is, because two-phase locking alone does not guarantee that you don't get dirty reads, you can lead to cascading abort. So this is the same schedule as before. We're going to access two different tuples. We're going to get the exclusive lock on A and the exclusive lock at B. That's because all the way down here, we're going to do operations on B. And we have to get all of our locks before we start unlocking anything, because we're in two-phase locking. So T1 does the operations it needs, releases lock on A. T2 gets the lock on A, does a read, and then also does a write. And then T1 aborts. But there's this problem now that T2 read a value from T1 that is now not actually in the system because the transaction aborted. It didn't commit. So we have this dependency now that because T1 aborted, you now have to abort T2 as well, because it read a value that isn't valid anymore. So that's what we mean by saying two-phase locking is susceptible to cascading abort. And the reason this is a problem is because you could be wasting a lot of work. So T2, because it read a value in T1, it could have done a bunch of operations on the system. And now because T2 has to abort as well, you're increasing the complexity of your rollback logic. More and more stuff needs to be rolled back in the system just because one transaction aborted leads to another transaction aborted. You can kind of see how this could domino through the system if you have a bunch of transactions running that are simultaneously reading values from each other and then one aborts. So another thing worth pointing out is this is a valid schedule under two-phase locking. Basically, kind of what I stated already, you can't let information from T1 leak to the outside world because it aborted, but it would be a permissible schedule under two-phase locking. So we're going to have to solve this somehow. And yeah, like I said, this is all wasted work.  So a couple other things about two-phase locking that we want to point out. There are schedules that are serializable that two-phase locking actually won't allow. Two-phase locking is a little bit more, I guess I'll say, conservative or, yeah, we'll use that word. Because there are serializable schedules that two-phase locking would not be allowed. And because of the locking, we're going to limit the concurrency of the system slightly. As I mentioned, we have this problem with dirty reads that can lead to cascading aborts. We're going to solve that with a modification to two-phase locking called strong strict two-phase locking. It's also called rigorous two-phase locking. The terms are kind of used interchangeably. I think the textbook refers to both. And we have this other problem which is common with a lot of locking protocols that we can lead to deadlocks. So we're going to need to solve that problem with either a detection mechanism where we can detect when we enter into a deadlock state or some sort of extension of the protocol where we prevent ourselves from ever being able to go into a deadlock state. So again, going back to what we did with latches, with concurrent data structures, we make sure we acquire the locks in a very specific way that makes it so that we can guarantee we're never going to enter into a deadlock scenario. So we're going to tackle the first one first. We're going to talk about strong strict two-phase locking. It's sort of a misnomer because the second phase doesn't really exist anymore. All strong strict two-phase locking says is you don't release any of your locks until the end of the transaction when you're going to commit. The growing phase is exactly the same. You keep acquiring locks as you need them. But you don't release any locks until commit time. And this is going to allow us to prevent any unrepeatable, or excuse me, any dirty reads from propagating across transactions, which is also going to solve the cascading aborts problem for us. So yeah, the figure is kind of updated. But as you can see, the shrinking phase, wherever you want to define it as the last lock you acquired is the start of the shrinking phase. But basically, there is no shrinking phase. Everything gets released at once at the end of the transaction. And the word strictness does have a specific meaning in when we're talking about concurrency control. It basically means anything that you wrote, none of your rights are going to be visible to any other area of the system, any other transactions, things like that, until you commit. So in this context, strict has a very specific meaning. And like I said, that solves our cascading aborts problem because no other transactions are going to see values that aren't committed to the system yet. So they're only looking at committed data. Everyone's happy. And like I mentioned before, this simplifies your abort logic because aborted transactions only have to put back their values. You don't need to worry about all these transactions in the system potentially reading uncommitted data. And then they each kind of have to store their own metadata almost or methods to be able to roll back the work that they've done. This simplifies things a lot by not having to reason about multiple versions in the system. You only have one undo to do if you wrote a value. So we're going to look at a simple example. Andy owes his bookie money, I guess. So he's going to move $100 to his bookie's account. And then the second transaction is just going to compute the sum. This echo command is made up. It's not a real statement. He just wants to demonstrate that you're reading these values out. We could have done something more complicated, but we want to keep the example fairly simple. First, we're going to look at it with, I think, just basic locking, then two-phase locking, and then strong-strict two-phase locking. So with the two locks that we defined at the beginning, and if we're not using two-phase locking, we can see, spoiler alert, you're going to get a wrong output from this. So we're going to start with $1,000 in each account. T1 already gets the exclusive lock, does a read because it needs to decrement his balance by $100. T2 wants to get the shared lock because it's trying to compute the sum. It can't get it, so it just starts waiting. T1 finishes its operation to decrement, unlocks A. At that time, T2 gets the lock on A and performs its read, and then unlocks it because, again, we're not in two-phase locking here. So you're free to acquire and release locks as you need them. T2 also gets the shared lock on B because it needs to do a write on that, which leads T1 to stall because it can't access that tuple yet. Eventually, T2 releases the lock. T1 gets the lock on B, finishes moving the money over to the bookies account, unlocks, commits. And T2 gives us a wrong output because it read an inconsistent state. It read part of the work that T1 had done, and T1 leaked that information to the rest of the system and said, OK, this bank account balance is $900, but that $100 was missing at that point when T2 came along and got the locks that it needed. So with two-phase locking, the key thing to notice here is it starts the same. T1 gets its exclusive lock on A, performs the operation it needs, but before it unlocks A, which would put it into the shrinking phase, it acquires the lock it needs on B all the way down here. So it gets the exclusive lock on B. T2's stuck waiting around, waiting for the locks that it needs. Because this isn't strong strict two-phase locking, T1 actually unlocks A. Well, yeah. If this were strong strict, it would be down here at commit time. So it unlocks A, finishes its operation on Object B, and then releases the locks. And this actually gives us a correct output. Same example with strong strict two-phase locking. Like I said, the unlocking happens at the end right before commit. So yeah, T1 gets its lock. T2 has to wait the entire time. And you can sort of see how strong strict two-phase locking is effectively forcing a serial ordering for these transactions. By basically acquiring all your locks, holding on to them until you get to commit time, you're guaranteeing that any of the operations that T2 would have that would conflict are going to be forced into serial ordering. Make sense? I'll go with yes. And then the correct output again as well. So I think Andy showed this slide before in the universe of schedules. He showed serial schedules, conflict serializable schedules, view serializable. And then I don't know if he had cascading of boards in there. So this slide actually I think is, he mentions it last year is actually incomplete. What he meant to show is where two-phase locking and strong strict two-phase locking live in this hierarchy. And I'll see if I can at least use the laser pointer because the clicker doesn't seem to be doing anything. Nope. So two-phase locking is going to live in this area right here. It's guaranteed to generate conflict serializable schedules, but it's going to be susceptible to cascading of boards. I'll see if we can get this slide updated for the ones that actually get published on the site so we actually see better than just a laser pointer on the video. But yeah, two-phase locking would be here. And then inside of this box and around serial you would have strong strict two-phase locking because it's guaranteed not to have cascading of boards. We get conflict serializable schedules. We're happy. Make sense? So now let's talk about the other problem with two-phase locking, which is that it leads to deadlocks. And like I mentioned before, there's a couple different ways we can try to solve this problem. We can be kind of lazy about it and use a detection algorithm to find a deadlock. Or we can kind of be a little bit more proactive about things and try to prevent them from ever happening in the first place. So you've probably seen deadlocks before. It's a fairly common concept in computer systems. But we'll give a basic example anyway. So T1 wants an exclusive lock on A. Lock managers like, sure, you can have that. No one else has that lock. T2 gets the lock on B because no one else has that lock. Now T2 says, I want the lock on A. Lock managers like, no, you don't get that. But T2 is going to sit around and wait for that lock. And T1 now wants the exclusive lock on B and it's going to wait as well. So we have a problem now. Both these transactions are waiting for locks that the other transaction holds. So we need to break this somehow. Nice animation. We have a problem. So like I said, deadlocks are a, when you have a dependency cycle between transactions where they're holding locks. And we have a couple of ways of dealing with them. Detection and prevention. Let's see what we got here. So with deadlock detection, what the system is going to do with like a background thread is you're basically going to look at the lock manager's metadata and you're going to build a wait's for graph. So the idea is every node is a transaction and every edge is pointing to another node that holds a lock that that transaction wants. This is all going to be done in the background. You can balance out how frequently this gets done. We'll talk about that in a minute. But the idea is it's a background task that inspects the state of the lock manager and says, hey, are there any deadlocks? Use your favorite cycle detection algorithm and you have to decide what you're then going to do with that deadlock. So let's look at a simple example of what these wait's for graphs look like. We'll start with T1 wants a shared lock that T2 already holds an exclusive lock for. So T1 gets an edge pointing to T2. T2 is going to end up with an edge pointing to T3 because, as you can see here, it wants an exclusive lock that T3 already holds a shared lock on. Those are incompatible, so we're just stuck waiting. And then lastly, T3 is going to end up with an edge pointing to T1 because it wants an exclusive lock that T1 has a shared lock on. We have a deadlock. So we have to do something about this. Questions? What's that? So deadlock handling is kind of simple. Pick a victim, kill it.  How far you roll that back is kind of implementation defined. It's possible you don't. I think this is a later slide. But you may not have to abort the entire transaction. You may not have to undo all the queries that it did. You may only need to partially roll back some of the queries to release the locks that you need to remove the deadlock and make forward progress in the system. The last point here is basically saying you have a trade-off in the system with how frequently you're going to build these wait for graphs. If the way you're dealing with deadlocks is detection, you have this background task. It's building these graphs, checking for deadlocks. It's up to you how frequently you want that task done. You could do it every microsecond if you want.  So what you may want to reason about is, OK, maybe I want to check for deadlocks less frequently. And if I do enter in a deadlock state, how long do I want to make those transactions wait? What's an acceptable timeout that I could sit in a deadlock state without detecting it right away? So these are always going to be tunable parameters in your database system, because different workloads are going to manifest different deadlock behaviors. So we want to make sure we're not being too aggressive. It may be fine just to leave the system in a deadlock state for 10 seconds even. It depends on what the responsiveness of the system needs to be. So victim selection, there's a lot of different things you can look at here. And certain systems get very elaborate with what they do. But there's all sorts of different heuristics you can look at. The first is, look at the age of the transaction when you're trying to find a victim that you're going to kill. Because at the end of the day, you have this cycle. You've got to pick one. You can start with the timestamp. You can look about how much work it's done. How many queries has it executed? Progress is a, you don't know how close it is to being done, but you can at least reason about how much work it's done. How many locks it already holds. Let's see. It's possible that you would have to look at the number of transactions. You have to roll back. That would be in the case of if you have cascading of boards. I don't think I mentioned before. You don't have to do strong, strict two-phase locking. Some systems may just do two-phase locking and live with the possibility of a cascading of board. Again, it's going to be workload dependent. You can decide if the workload is not very susceptible to cascading of boards or dirty reads. You can just say, two-phase locking is fine. I can release my lock sooner. That may result in higher throughput in the system. Again, that's often going to be something that's configurable. Why would you make about the transaction the lowest time? Shouldn't you about the one with the highest time? How has that in the least work been done? Yeah, so we'll talk about a couple different ways to decide a little bit later on about which transaction gets killed. There are a couple different solutions, but the other one is typically going to be a combination of things. It's not necessarily always going to be just the age of the transaction. It's going to be what determines whether you get killed or not. Often, one of the biggest things is going to be the number of times you've already been killed, which is the last one says there. Because you do want to make sure you make progress in the system. You need to prevent starvation for these transactions. And I'm not saying any one of these is exactly what you're going to use to kill a transaction. The commercial systems get rather elaborate and can build predictive models even to try to figure out which transaction they should try to kill when they enter a deadlock scenario based on all these different heuristics. So it's typically a combination. It's not just the time stamp, I guess. It's a long way of answering, hopefully, your question. My question is, why would you always want to kill? Should it be considered the high? Even if you're considering only the time stamp, then why not the highest? Like, the one where it's been for the least time? If someone is working for like one hour and that's almost everything and just wants to write on the answer variable, answer it open, and it is just waiting for that lock. If you kill that, then all that one hour will be wasted. OK, so your question is, why would you? Sorry, I want to make sure I repeat it back for the video. Your question is, why would you always want to kill?  Yeah, I suspect it's still always going to depend on the workload, depending on what's causing the deadlock, what sort of situation is causing it. But yeah, we'll formalize a little bit like in a few slides on what gets killed and why. But I think in the case of Postgres in the example, we'll see the highest time stamp gets killed. I think it's just an example of just saying, like, one heuristic you could look at. And then like I mentioned before, it's possible you don't need to abort the entire transaction. You could completely abort it, or you could do some sort of minimal abort, where you only roll back the number of queries in that transaction that will allow you to release the deadlock and make forward progress in the system. Again, this is something that's going to depend on the workload, how much work you're throwing away, and whether it just makes sense to have transactions resubmit all their queries, or if you can slowly unwind parts of it in order to free the system up from the deadlock and make forward progress. Let's see if we can get a demo working. So pardon my typing on this Surface keyboard. Looks like it's working. So we'll start with my SQL. We'll have two terminals open. We have a pretty basic table set up called transaction demo. That's horrible. You can't see what's happening there. But basically, we have two tuples in it, primary key ID 1 and 2, and the values 100 and 200. Pretty simple. We have two tuples. This should be really easy to put us into a deadlock state. What's up? I have no idea which ones those are. Usually, if you press all the buttons, it kind of works. I'm glad I was standing next to you. We got that on tape. Producer won, everyone. So we've got these two tuples in our table transaction demo. First thing we're going to do is set our timeout here.  So in this case, we're going to try to change the lock weight timeout here. In ODB is the storage engine for my SQL as of, I don't know, my SQL 5, I think, or something like that. So the first terminal will begin a transaction. The first thing we're going to do is try to update the value of tuple 1. Actually, I missed a step. We need to explicitly tell my SQL we want to run in the serializable isolation level. So that's going to give us complex serializable schedules. We haven't talked about isolation levels yet. Like I said, I think that's going to be in the next lecture. But we need to tell the system we want serializable isolation level. We'll go back. We'll begin our transaction. And we're going to, like I said, update the value on tuple 1. Switch to our other terminal. We're going to start a transaction. We're going to update the value on tuple 2. So so far, no conflicts yet. T1 holds a lock on tuple 1. T2 holds a lock on tuple 2. We're going to go back to transaction 1 here. And we're going to say, OK, I want to update the value on where ID is 2. And it just waits. All it can do is stall. It's waiting for the lock. And then we can go back to T2 and say, I want to update the value on 1. T1 holds that lock already. We should get a deadlock. And sure enough, we get a deadlock. MySQL yelled at us rather quickly. Not a ton of debugging information, but deadlock found we're trying to get the lock. Try restarting the application. And you'll notice that T1, which was waiting for the lock on value 2, as soon as the system detected a deadlock with T2, it killed that transaction. T1 got the lock and made the progress that it needed. This is not strong fit, right? Because there was no isolation. 41. What do you mean by there was no isolation? It used that updated value on T2. We can check. So I'm just going to roll both of these back. And we'll begin. And we're going to update the value in ID1. We'll go down to T2, begin a transaction, update the value where ID is 2. And then we're going to just do a select star from transaction demo in T1 so it can't get the lock. And it's just going to hang there. If we do a rollback, we see that the value of T2 was never written to the table because it didn't commit. And T1 was able to make its forward progress by getting the shared lock. Cool? Good question. Yeah? There is no isolation. Yeah, I mean, in practice, you're probably going to see strong strict two phase locking because the system can't reason about whether it should sort of eagerly start releasing locks because it doesn't know what the client's going to do next. So you could, but I don't think any systems do this, you could speculate about starting to release locks early, but you might get it wrong. And we can't be wrong. We have to make sure we guarantee these isolation guarantees. Oh, yeah, I guess so. OK, isolation can stay in some boxes. Sure. Well, that's not actually true. So like I said, we'll talk about isolation levels in the next lecture or two. There are scenarios where you may be willing to run with lower isolation guarantees. And you can start to introduce certain anomalies back into the system if you are comfortable with them. Or if at the application layer you have your own logic to reason about that sort of stuff, you can lower isolation guarantees if it improves the performance of your system. But if we want to guarantee that, well, I think that way is through the stronger component of the big lock. Yeah, so strong strict two-phase locking or rigorous two-phase locking is the one we need to use if we want to guarantee we don't get any dirty reads that could result in cascading of warts as well. OK, cool. So that's my sequel. We can take a look at Postgres real fast. And we'll do a similar demo. Postgres, there is one other cool thing we can do here. So we're going to set the deadlock timeout to 10 seconds. The demo itself is, let me just make sure I created this table. Cool, same table exists, two values. With Postgres, we're going to begin each transaction by saying we want isolations level serializable. It's not necessarily a session-wide setting. You can actually, at a transaction granularity, define which isolation level you want to run at. And again, I keep saying isolation levels. We'll talk about that in a future lecture. T1 is going to begin. Oh, very begin. That's right. We're just going to do the same operation. We're going to update value where ID is 1. We're going to go down here. Do the same thing where value equals 2. Go back to T1. Try to update 2. We wait, looking for the lock that we don't have yet. And then we're going to force a deadlock by asking T2 to update the value related to 1. And there was a slight lag there. I don't know if you saw it. But compared to MySQL, which instantly was like, this is a deadlock, Postgres waited a little bit before it yelled at us and said, hey, you have a deadlock in the system. So like I said, these are tunable parameters. You can adjust how aggressive the database system is going to be with looking for deadlocks. Postgres gives us a lot more information. Tells us exactly which process was waiting for what kind of lock held by another transaction, which is another process. Postgres, I think this is a big reason Andy loves referring to Postgres, because it's basically a textbook implementation. The wording you get back out of the system is exactly what you would see out of a database textbook and kind of the behavior that would help us kind of understand what's going on here. There's one more thing we can try. I'm getting low on time, but I will do one more thing. So I'm going to roll both of these back. I'm going to set the timeout to 20 seconds, because there's one more thing we want to try to see. Let's see if I can get one more terminal. Hopefully this still plays. It may be tough. The deadlock timeout, we'll do the same thing. We can transaction in serializable on two of the terminals. This is going to be a slight race against the deadlock detection algorithm. So what I'm going to try to do here is demonstrate some of the internal metadata that Postgres presents to you and how it's tracking these sorts of things. Do that. And did my Tmux session just die? It died. Does this still have a connection? No. Interesting. Well, that's that demo. Cool, we're low on time anyway. But the idea is if we had been able to put the system into a deadlock state, I was going to query the catalog, and it was going to be able to show which transactions were holding locks, which locks they're waiting on, which transactions and processes those belong to. Postgres is great. Let's go back to this stuff. So like we talked about before, the alternative to doing a detection algorithm is to prevent yourself from ever going into a deadlock state in the first place. If we can come up with some sort of system that prevents this from happening, we don't never need to build weights for graphs. We don't need this background task that needs to make decisions about which process we're going to kill or which transaction we're going to kill. And I suspect this is probably what my SQL was doing, because my SQL immediately yelled at us that we had a deadlock, or immediately detected it, didn't prevent the deadlock, but it immediately detected it as opposed to Postgres, which kind of had a bit of a lag to it with the detection. So a very simple way to do this is to assign priorities based on timestamps. So older transactions are considered higher priority. And then you kind of have two different protocols you can follow. You have this weight die system, which basically just means old transactions wait for young transactions. So if the requesting transaction has a higher priority, it's going to hold a transaction. So a holding transaction is younger than it, but the older transaction wants its lock, then the requesting transaction is just going to sit around and wait. Otherwise, the requesting transaction is going to abort. The other system is called wound weight. You can think of it as young weights for the old. Requesting transactions, if they have a higher priority, then the holding transaction, the holding transaction abort. So basically, the requesting transaction comes in, steals your lock, and makes forward progress. Alternatively, the requesting transaction just waits. So this is probably confusing. It's a little bit clearer, I think, on an example on the next slide. So we have these two scenarios, these two schedules we want to look at. We'll look at the first one first. T1 and T2. T1 begins first, so T1 is going to have the higher priority than T2. But T2 gets the lock first. So T2 gets an exclusive lock on A. T1 wants the exclusive lock on A. It's the older transaction. It has higher priority. So under weight die, T1 is going to wait for that lock. And under wound weight, T1 is basically going to come in, kill T2, steal its lunch money, and T2 has to restart or whatever it needs to do, whatever the application needs to do with that transaction. In the second schedule, T1 once again begins before T2. So it has an older timestamp. It's higher priority. But it got the lock first. T2 wants that lock under weight die. T2 is going to abort. And under wound weight, T2 is just going to wait. So what these two policies are doing is they're basically defining the same sort of logic that we used for latching in concurrent data structures where this concept of we only take latches in one direction and we release them in a disciplined manner as well. If you can apply some sort of ordering to how you acquire your locks, which is effectively what this is doing, you can avoid deadlocks entirely. So weight dies basically saying, OK, we're going to order our timestamps in one direction and how we hand out locks. And then in wound weight, it's basically just the opposite. As long as we're consistent, you can't mix these things. If you start mixing them, you're going to get deadlocks in why you're even doing this, because then you would need a deadlock detection algorithm and you didn't solve any problems. Why is it called wound weight instead of deadlock? So the question is, why is this called wound weight as opposed to some sort of opposite of weight die? I have no idea. I really don't like the naming of this. I find it very confusing. Yeah, if you're confused by the naming, I'm with you. I think that's why Andy kind of adds this sort of simplification of old weights for young or young weights for old, because yeah, weight die, wound weight, it's a weird nomenclature. Any other questions? So this is sort of what I was talking about before. These schemes guarantee no deadlocks, because you're basically enforcing locks being handed out in a single direction or locks even being able to be stolen in a single direction. So yeah, we've talked about this already. So when a transaction restarts, what would its new priority be? Any guesses? What would its time stamp be? It's got to be the original. Any guesses why? So then you maintain the consistency of whether or not it gets wounded or died or weighted, whichever one. Essentially, if you have a transaction you start and then it gets killed by another transaction coming in, let's say because its priority will increase, the older it is, you want to maintain its true age as opposed to just resetting its age. Right, so that's right. In one word, starvation. We can't have transactions being starved. So when you restart the transaction, we want to make sure it's still used with the same time stamps so that eventually, whichever deadlock prevention ordering we're going with, we want to make sure progress gets made at one point or another. So we have 10 minutes. So we'll see how far we get. So far, this doesn't seem very efficient. What if we have a transaction that needs to update a billion tuples? Are we going to the lock manager a billion times? Going to the lock manager is not cheap. You're not implementing one this semester, but you can envision if you had to implement this in a system, you're going to need data structures that are protected by latches. It has to be concurrent. It's on the critical path of what every transaction is doing. We can't make all these trips to the lock manager. So what we're going to try to do is introduce some sort of hierarchy or change the granularity or allow different granularity of locks in the system. So we can apply locks to tuples, pages, tables. If you knew you needed to do a billion updates on a single table, maybe it makes sense to just ask for the single exclusive lock on the entire table, you are going to prevent other transactions from accessing it, but maybe that's what you want to do. The goal is to reduce the number of total trips to the lock manager possible by using this hierarchical model. So to do that, we're going to have to introduce a few more lock types and then this notion of a hierarchy. So conceptually, like I was describing, you can now take locks at different levels in the system. So a database can have multiple tables, different tables can have multiple tuples, and within different tuples, you can have multiple attributes. So you want to acquire the fewest number of locks possible to do the work that you need to do. So if T1 takes the lock on table one, if it explicitly takes a lock on table one, it now implicitly locks everything below it in this tree. This tree's, again, it's a slightly abstract concept. This isn't like a B plus tree or something like that. It's just in the hierarchy of the database system of all the tables and tuples you've inserted into the system. It's sort of a conceptual hierarchy in the system. So Andy's trying to get the balance of his shady offshore bank account and he's giving me 1% interest at the same time. Cool. This is sort of demonstrating in this conceptual tree that we have, what sort of locks do we need to take? We have all sorts of different types of locks. We need exclusive locks because my bank account balance is changing, Andy's going to need shared locks. And then there's this notion of an intention lock, which I think is defined on the next slide. You basically give hints in this tree. You basically take intention locks at higher levels to give hints to other transactions about what you're doing in lower levels of the system that's going to try to increase the parallelism of the system. So like I said, an intention lock is a hint to other transactions and it'll get clear with an example, but the basic idea is if you have an intention shared lock in this tree, the subtree rooted at that node, there is an explicit shared lock somewhere underneath that. Same thing with an intention exclusive lock. Somewhere in that subtree rooted in that node, there's an explicit exclusive lock. We're gonna add three more lock types, intention shared, which is like I described. Somewhere in the subtree rooted at that node, there's an explicit shared lock. Intention exclusive, same idea. Somewhere in that subtree there's an explicit exclusive lock and then there's one kind of tricky one, shared intention exclusive. This is an explicit shared lock on this node, which means that you have shared lock on everything below it in its subtree. And somewhere in that subtree, you also have an explicit exclusive lock. So you can imagine if you wanted to do a read on an entire table and then maybe update one value, you would get a shared intention exclusive on that table because you're gonna take a shared lock on the entire table and say, I'm reading all the values of this table, but I'm only gonna update one value. So you have an exclusive lock on one tuple further down. It'll make more sense with an actual example, I think, but are there any questions first? Example. Our compatibility matrix gets a little bit more complicated. Not a lot to say about this other than I think the tree actually seeing an example is a little bit more clear, but basically, the semantics are similar to shared and exclusive lock compatibility just apply to it to a tree. The lock protocol is basically just saying in order to get a shared lock, you have to hint at least in the parent node that you have an intention shared lock. Same thing with exclusive, to get an exclusive, intention exclusive, shared intention exclusive. You have to at least hold an intention exclusive on the parent node. Again, let's do an example because I think that's gonna make things a little bit clearer, we have five minutes. Very simple table, or a very simple example.  We wanna check Andy's bank account. He wants to do a read on tuple one. So he wants, he's gonna want just a shared lock on this one to do a read. But we're gonna have to take an intention shared at the parent node first. Basically as a hint to say, hey, below this node, I'm gonna take an explicit shared lock. T2 comes along, we wanna update my bank balance by 1%. So we want an explicit exclusive lock on this tuple. So we're gonna try to get, yeah, we get our intention exclusive lock on the parent node and we get our exclusive lock on the individual tuple. Now things will get a little bit more interesting.  T2's gonna read a single tuple, T3's gonna scan all of them. I apologize for going a little fast. I realize this is probably a little confusing, but we're getting a long time. So T1, like I said, it wants to read all the tuples and do an update on one. So it's gonna get a shared intention exclusive. This means I'm taking a shared lock on the entire table so I can read all the attributes and all the tuples in this table. And the intention exclusive part means I'm gonna update at least one of these tuples down there. In this case, it's tuple n. So because this is shared intention exclusive, all these tuples are implicitly locked in shared mode and then this is the only one we actually have to take an exclusive lock on because that's the only one being updated. T2 wants to read a single tuple. We're gonna need the shared lock on this guy which means we need intention shared at this level. That's all good, we can do that. The last transaction is the one that's gonna have problems. It wants to do a read on all of them. It's gonna want a shared exclusive shared lock, or excuse me, explicit shared lock on the table. Can't get that because it's not gonna be compatible with the shared intention exclusive because there's a right happening lower in the table. T3 has to wait. So basically it wants this explicit shared lock on the table. Can't have that, all it can do is wait. Yeah. I am confused with this example. Like two operations are understood. First was to read tuple one, to read the bank account. And the second operation was to increase one percent, was to add it to the tuple length. How did this thing, reading all the tuples came into the picture? So I may have gone too quickly. The example operations changed when I got to this one with three transactions. This is no longer doing like the read Andy's bank account, bump my account balance by one percent. T1's reading all the tuples and then modifying one. T2's just doing a read on a single one. And then T3 is the one that's trying to read the entire table. So yeah, it's a different example. I'm sorry about that if I went kind of quickly. So in practice, it seems complicated, but it's actually pretty helpful because you can reduce the number of locks that go, or the number of trips to the lock manager. You reduce the number of lock requests dramatically. And like we mentioned before, there's this concept of lock escalation. So if you already have locks in shared mode and you want to bump them to exclusive locks because you've decided you want to do it right on the tuple, you can do that. Once again, this is designed to reduce the number of trips to the lock manager and also doesn't violate two page locking because you can upgrade your locks, you don't actually release the lock. So in practice in real systems, you're not sitting there telling it which tuples to lock.  So it's not part of the SQL standard, but here's examples on how to do it in like Postgres Oracle, DB2, SQL, MySQL. These guys all use the nomenclature that we're learning about now, which is shared and exclusive. And because MySQL loves to be different, they call them read and write locks because they want to be different. There's also this notion of select for updates.  I'm going to do a write later on, just take the exclusive lock now and hold it for me. So you can do select and add this for update that basically tells the system take the write lock or excuse me, take the exclusive lock right now. And you can also tell it just to take a shared lock. I don't know why you would ever do that because by default, it probably should just take a shared lock if you're doing a read. To finish things up, like the slide says, it's used in almost every system out there, at least most widely deployed commercial systems, SQL server, MySQL, Postgres. Two-phase locking is a big deal, but it's also not too difficult to implement and it gives us exactly what we want. It gives us our serializable schedules. We just have to be disciplined about whether we're going to try to detect our deadlocks and handle them, or we're going to try to prevent them entirely in the first place. Next class, I think it's going to be Dana talking to you guys about timestamp ordering, which is good. Cool. Oh yeah, coming through with my shell and crew. Two cent for a case in me, St. I's crew. In the midst of broken bottles and crushed up can. Met the cows in the gym or I'll drive. It's with St. I's in my system. Crack another unblessed. Let's go get the next one and get over. The object is to stay sober. Lay on the sofa. Better yet, damn I'm sure. Come here to follow me, Tim. Stressed out. Could never be sun. Rick is a jelly. Hit the deli for a part one. Naturally blessed. Yes. My rap is like a laser beam. The puns and the bushes. St. I's been licking the king. Crack the bottle of the St. I's. Sipping through gold. You don't realize. I'm drinking it. Only two we drunk. You can't drive. Keep my people still alive. And if the St. don't know, you're full of can of pain.