1 00:00:06,570 --> 00:00:09,120 - This is the last basic pattern I'm gonna show you, 2 00:00:09,120 --> 00:00:10,970 and then we're gonna start looking at some higher level 3 00:00:10,970 --> 00:00:14,200 code that uses all of these patterns, but it's cancellation. 4 00:00:14,200 --> 00:00:17,410 Cancellation deadlines are critical to software 5 00:00:17,410 --> 00:00:20,580 because a task or a request cannot take forever. 6 00:00:20,580 --> 00:00:23,220 Now, we're really gonna use the context package 7 00:00:23,220 --> 00:00:25,790 that's part of the standard library for this, 8 00:00:25,790 --> 00:00:27,690 so I'm gonna use this code to show you 9 00:00:28,728 --> 00:00:32,200 some basic mechanics of how the context package does things. 10 00:00:32,200 --> 00:00:33,033 Okay? 11 00:00:33,033 --> 00:00:35,260 But ideally I wouldn't want to see this code in production, 12 00:00:35,260 --> 00:00:37,630 I'd wanna see the use of the context package. 13 00:00:37,630 --> 00:00:39,460 And I'll show you that next. 14 00:00:39,460 --> 00:00:40,850 So, here's cancellation. 15 00:00:40,850 --> 00:00:43,210 Notice that we're using a buffered channel of one, 16 00:00:43,210 --> 00:00:45,740 we're getting some delayed guarantees here. 17 00:00:45,740 --> 00:00:47,910 We're still signaling with string data, 18 00:00:47,910 --> 00:00:50,240 we're gonna get some delayed guarantee, right? 19 00:00:50,240 --> 00:00:52,820 Not a full guarantee, but also we're not gonna be 20 00:00:52,820 --> 00:00:55,840 completely blind to when things happen. 21 00:00:55,840 --> 00:00:58,440 Now, we do this again, here we are, 22 00:00:58,440 --> 00:01:00,470 here's our, we are the manager, 23 00:01:00,470 --> 00:01:02,430 we've got our path of execution, 24 00:01:02,430 --> 00:01:04,930 we've created this buffered channel of one, right? 25 00:01:04,930 --> 00:01:06,710 We've got a buffered channel of one. 26 00:01:06,710 --> 00:01:10,243 And we go off, and we, again use that pattern 27 00:01:10,243 --> 00:01:12,940 of wait for result, right? 28 00:01:12,940 --> 00:01:15,780 We start this employee goroutine, the goroutine 29 00:01:15,780 --> 00:01:20,530 knows exactly what work it is they have to do. 30 00:01:20,530 --> 00:01:23,290 The key is is that this might be work 31 00:01:23,290 --> 00:01:25,470 that one day we wanna cancel. 32 00:01:25,470 --> 00:01:28,130 We might wanna cancel this work, 33 00:01:28,130 --> 00:01:31,570 you see, if we do the work, if we do the work, 34 00:01:31,570 --> 00:01:33,610 then we can't cancel it. 35 00:01:33,610 --> 00:01:36,850 We can't 'cause we're blocked waiting for it to get done. 36 00:01:36,850 --> 00:01:38,790 This is where we need another goroutine 37 00:01:38,790 --> 00:01:40,470 if we wanna be able to cancel. 38 00:01:40,470 --> 00:01:42,950 We can monitor how well we're doing, 39 00:01:42,950 --> 00:01:46,120 and then tell this other goroutine, "Dude, stop." 40 00:01:46,120 --> 00:01:47,570 So, this is what happens. 41 00:01:47,570 --> 00:01:50,470 We do the work, and if the work finishes, 42 00:01:50,470 --> 00:01:53,360 then this goroutine wants to perform the send, right? 43 00:01:53,360 --> 00:01:57,180 We're looking to perform the send back to us, 44 00:01:57,180 --> 00:01:59,370 and that send can happen automatically 45 00:01:59,370 --> 00:02:00,600 because there's a buffer. 46 00:02:00,600 --> 00:02:02,480 So, that data can go right in here, 47 00:02:02,480 --> 00:02:04,950 we don't need a corresponding receive, 48 00:02:04,950 --> 00:02:06,960 we've got a buffered channel of one. 49 00:02:06,960 --> 00:02:07,793 So, here we are. 50 00:02:07,793 --> 00:02:10,460 Let's imagine right now, we're on this work piece 51 00:02:10,460 --> 00:02:13,100 right there, line 227, we don't know how long 52 00:02:13,100 --> 00:02:14,900 this work is going to take, 53 00:02:14,900 --> 00:02:18,370 but we've decided the following on line 232. 54 00:02:18,370 --> 00:02:22,890 After we created the employee, we set up the TC variable, 55 00:02:22,890 --> 00:02:26,620 and the TC variable says that this work has 56 00:02:26,620 --> 00:02:30,670 100 milliseconds of time to get done. 57 00:02:30,670 --> 00:02:34,370 If you go back to line 227, I've simulated that this 58 00:02:34,370 --> 00:02:37,251 could potentially take anywhere from 59 00:02:37,251 --> 00:02:40,920 one to 500 milliseconds, okay? 60 00:02:40,920 --> 00:02:43,880 So, there's a good chance that 61 00:02:43,880 --> 00:02:45,860 this work doesn't get done in time. 62 00:02:45,860 --> 00:02:49,410 We're not willing to wait more than 100 milliseconds, 63 00:02:49,410 --> 00:02:52,160 if that happens, we've got to move on, I'm sorry. 64 00:02:52,160 --> 00:02:54,180 How do we set up that scenario? 65 00:02:54,180 --> 00:02:56,430 Well, we go back to the select because the select 66 00:02:56,430 --> 00:02:59,300 allows us to handle multiple channel operations, 67 00:02:59,300 --> 00:03:01,690 both sends and receives at the same time. 68 00:03:01,690 --> 00:03:03,080 So, look at the first case. 69 00:03:03,080 --> 00:03:07,400 The first case is a channel receive, okay, 70 00:03:07,400 --> 00:03:08,560 off the buffer. 71 00:03:08,560 --> 00:03:13,290 So, here we are blocked on a receive, on a receive, 72 00:03:13,290 --> 00:03:16,380 where we wanna receive off of this buffer, 73 00:03:16,380 --> 00:03:18,790 and as soon as some data gets in there, 74 00:03:18,790 --> 00:03:20,270 then we'll unblock. 75 00:03:20,270 --> 00:03:25,030 But the next case is a channel receive on our timer. 76 00:03:25,030 --> 00:03:27,570 I've got two channels right here, 77 00:03:27,570 --> 00:03:29,610 one trying to receive on this, 78 00:03:29,610 --> 00:03:33,980 and one trying to receive on our timer. 79 00:03:33,980 --> 00:03:38,020 That call to time dot after, what it returns is a channel, 80 00:03:38,020 --> 00:03:41,220 and it's a channel that the run time will add the current 81 00:03:41,220 --> 00:03:43,430 time to after the duration of 82 00:03:43,430 --> 00:03:45,980 100 milliseconds in this case is complete. 83 00:03:45,980 --> 00:03:50,060 So, the run time, the run time, okay, 84 00:03:50,060 --> 00:03:53,507 will come in, the run time will come in on our timer, 85 00:03:53,507 --> 00:03:56,790 and this is our timer here, the run time 86 00:03:56,790 --> 00:03:58,990 will come in on our timer, and then perform 87 00:03:58,990 --> 00:04:01,960 the send for us at 100 milliseconds. 88 00:04:01,960 --> 00:04:06,872 Or, this send right here will add data to the buffer, 89 00:04:06,872 --> 00:04:10,310 which will allow this receive to unblock. 90 00:04:10,310 --> 00:04:11,870 Which one's gonna happen first? 91 00:04:11,870 --> 00:04:14,160 I have no idea, that's the point. 92 00:04:14,160 --> 00:04:17,490 If it happens, if this send happens 93 00:04:17,490 --> 00:04:19,930 within 100 milliseconds, guess what? 94 00:04:19,930 --> 00:04:21,440 We execute this case. 95 00:04:21,440 --> 00:04:24,110 If it doesn't happen within 100 milliseconds, 96 00:04:24,110 --> 00:04:25,770 then we're gonna execute this case. 97 00:04:25,770 --> 00:04:29,050 Either way, we have a guarantee that we are going to move 98 00:04:29,050 --> 00:04:31,730 on within 100 milliseconds or less. 99 00:04:31,730 --> 00:04:34,260 Now, there's a big, big, big caveat to 100 00:04:34,260 --> 00:04:36,210 this pattern here I want you to see. 101 00:04:36,210 --> 00:04:38,627 There is a bug that we would be creating 102 00:04:38,627 --> 00:04:41,570 if I used an unbuffered channel. 103 00:04:41,570 --> 00:04:45,112 If I used an unbuffered channel, we would have a very, 104 00:04:45,112 --> 00:04:48,520 very big problem in this piece of code. 105 00:04:48,520 --> 00:04:50,600 Let's walk through a scenario, 106 00:04:50,600 --> 00:04:53,500 where if I do not use my buffered channel of one, 107 00:04:53,500 --> 00:04:57,090 life gets very, very bad very quickly. 108 00:04:57,090 --> 00:04:59,230 Okay, so here we are. 109 00:04:59,230 --> 00:05:00,400 We've got our goroutine. 110 00:05:00,400 --> 00:05:03,180 Now, the channel says that we want guarantees, right? 111 00:05:03,180 --> 00:05:05,100 Absolute guarantees that a signal 112 00:05:05,100 --> 00:05:06,840 being sent has been received. 113 00:05:06,840 --> 00:05:10,080 So, we go off, and the employee is created, 114 00:05:10,080 --> 00:05:13,120 it goes off and does some work, there it is. 115 00:05:13,120 --> 00:05:16,670 Now, we are on there, on line 234, 116 00:05:16,670 --> 00:05:18,330 we've got two situations again. 117 00:05:18,330 --> 00:05:21,550 Remember, we can receive, okay, 118 00:05:21,550 --> 00:05:24,720 we've got the channel receive from this employee, 119 00:05:24,720 --> 00:05:27,350 so we're waiting, we're waiting on our receive 120 00:05:27,350 --> 00:05:32,350 from the employee, or we've got our time out channel, right? 121 00:05:32,500 --> 00:05:36,280 Our time out, where we're gonna potentially receive 122 00:05:36,280 --> 00:05:39,420 directly from the run time. 123 00:05:39,420 --> 00:05:41,720 We don't know which one's gonna happen first, 124 00:05:41,720 --> 00:05:44,870 but let's say that the time out happened. 125 00:05:44,870 --> 00:05:47,270 Let's say the time out happens, right? 126 00:05:47,270 --> 00:05:51,740 If the time out happens, then we receive and send here, 127 00:05:51,740 --> 00:05:53,690 what happens to this goroutine? 128 00:05:53,690 --> 00:05:57,496 This goroutine then moves on, it moves on, 129 00:05:57,496 --> 00:06:02,300 which means that we're no longer in this receive anymore. 130 00:06:02,300 --> 00:06:05,920 We move on, and what happens when we move on? 131 00:06:05,920 --> 00:06:08,973 Well, eventually this work is going to get done, 132 00:06:08,973 --> 00:06:12,360 and when that work gets done on line 227, 133 00:06:12,360 --> 00:06:16,560 this goroutine is gonna wanna perform a send. 134 00:06:16,560 --> 00:06:18,860 It's gonna wanna perform a send, 135 00:06:18,860 --> 00:06:20,910 but the send cannot complete without a 136 00:06:20,910 --> 00:06:24,310 corresponding receive, we're using an unbuffered channel, 137 00:06:24,310 --> 00:06:26,320 where we wanna guarantee that the signal being 138 00:06:26,320 --> 00:06:27,830 sent has been received. 139 00:06:27,830 --> 00:06:32,380 We are going to block forever, why? 140 00:06:32,380 --> 00:06:35,890 Because there's no goroutine anymore that's receiving 141 00:06:35,890 --> 00:06:38,510 on the channel, this blocks. 142 00:06:38,510 --> 00:06:40,820 Whew, this is a goroutine leak, 143 00:06:40,820 --> 00:06:43,890 this is a classic goroutine leak. 144 00:06:43,890 --> 00:06:46,390 Goroutine leaks are gonna eventually cause issues 145 00:06:46,390 --> 00:06:51,240 around memory, right, you're gonna start leaking memory. 146 00:06:51,240 --> 00:06:53,310 But Go is really good at cleaning things up, 147 00:06:53,310 --> 00:06:56,440 so sometimes these goroutine leaks don't show themselves 148 00:06:56,440 --> 00:06:59,832 for hours, days, or weeks because Go is very good 149 00:06:59,832 --> 00:07:02,980 at dealing with and minimizing memory. 150 00:07:02,980 --> 00:07:05,490 But understand that when we deal with these 151 00:07:05,490 --> 00:07:09,400 cancellation patterns, where the goroutine here, 152 00:07:09,400 --> 00:07:13,040 us M, could walk away from the goroutine here 153 00:07:13,040 --> 00:07:15,450 that's doing the work, and there's no way to tell 154 00:07:15,450 --> 00:07:17,350 that goroutine to stop directly, 155 00:07:17,350 --> 00:07:19,760 sometimes there is through networking calls, 156 00:07:19,760 --> 00:07:21,270 but other times there isn't. 157 00:07:21,270 --> 00:07:24,920 We've gotta make sure that that goroutine doesn't leak 158 00:07:24,920 --> 00:07:27,470 by using the buffered channel of one. 159 00:07:27,470 --> 00:07:31,870 So, this time, that send doesn't need a corresponding 160 00:07:31,870 --> 00:07:35,171 receive, we've got a buffered channel here, 161 00:07:35,171 --> 00:07:40,171 which means the send happens before the receive. 162 00:07:40,260 --> 00:07:42,050 Okay, so these are things we're gonna look at. 163 00:07:42,050 --> 00:07:44,510 And if you can visualize goroutines as people, 164 00:07:44,510 --> 00:07:47,340 and visualize this idea of manager, employee, 165 00:07:47,340 --> 00:07:49,120 put yourself always as the manager. 166 00:07:49,120 --> 00:07:51,680 Think about these goroutines as doing work on your behalf, 167 00:07:51,680 --> 00:07:54,541 you can start visualizing this orchestration right, 168 00:07:54,541 --> 00:07:57,480 the interactions between these goroutines, 169 00:07:57,480 --> 00:07:58,870 then you can start seeing whether or not 170 00:07:58,870 --> 00:08:00,530 you're gonna block, whether or not you're gonna 171 00:08:00,530 --> 00:08:02,370 deal with a high levels of latency, 172 00:08:02,370 --> 00:08:03,750 or maybe you can reduce that. 173 00:08:03,750 --> 00:08:06,360 We can use a select case to deal with drop patterns. 174 00:08:06,360 --> 00:08:08,510 And we can start doing a whole bunch of stuff 175 00:08:08,510 --> 00:08:11,340 once we start thinking about our signaling semantics 176 00:08:11,340 --> 00:08:13,480 and visualize the interactions these 177 00:08:13,480 --> 00:08:14,930 goroutines are going to have.