1 00:00:06,570 --> 00:00:08,640 - Let's take everything we've done right now 2 00:00:08,640 --> 00:00:12,680 and summarize it in a cool little kinda live coding demo, 3 00:00:12,680 --> 00:00:14,470 so you can see how these concurrency patterns, 4 00:00:14,470 --> 00:00:17,110 orchestration, buffer channels, 5 00:00:17,110 --> 00:00:20,007 how we can leverage this to do some really nice things, 6 00:00:20,007 --> 00:00:22,280 things that we couldn't do in other languages 7 00:00:22,280 --> 00:00:24,530 as simple as we can now do in Go. 8 00:00:24,530 --> 00:00:27,860 When we are maintaining our signaling patterns 9 00:00:28,970 --> 00:00:32,850 appropriately, really our signaling semantics appropriately 10 00:00:32,850 --> 00:00:34,070 for the problem. 11 00:00:34,070 --> 00:00:36,030 So here is the problem. 12 00:00:36,030 --> 00:00:39,740 Let's say that we had an application that's writing logs. 13 00:00:39,740 --> 00:00:44,270 And this application is using the standard library's logger, 14 00:00:44,270 --> 00:00:46,030 which I use all the time. 15 00:00:46,030 --> 00:00:49,150 I honestly use the standard library logger all the time. 16 00:00:49,150 --> 00:00:52,020 So I don't necessarily need logging levels. 17 00:00:52,020 --> 00:00:54,330 I really focus on signaling, 18 00:00:54,330 --> 00:00:56,730 or signal versus noise in my logs. 19 00:00:56,730 --> 00:00:58,060 And again, I use structured logging. 20 00:00:58,060 --> 00:00:59,690 I really don't when I don't have to. 21 00:00:59,690 --> 00:01:01,250 So here it is, here's this app. 22 00:01:01,250 --> 00:01:03,560 Let's say it's handling hundreds of thousands 23 00:01:03,560 --> 00:01:04,550 of video requests. 24 00:01:04,550 --> 00:01:06,800 Let's say that we're streaming videos. 25 00:01:06,800 --> 00:01:08,730 So we got a socket connection in. 26 00:01:08,730 --> 00:01:10,577 We stream video to that device 27 00:01:10,577 --> 00:01:12,810 and we log about that device. 28 00:01:12,810 --> 00:01:16,470 So let's simulate maybe a program that's doing this 29 00:01:16,470 --> 00:01:18,470 where we're using the standard library logger 30 00:01:18,470 --> 00:01:20,920 and we're letting every Go routine, all 10,000, 31 00:01:20,920 --> 00:01:23,560 write to standard out directly. 32 00:01:23,560 --> 00:01:24,720 Okay, brilliant. 33 00:01:24,720 --> 00:01:28,050 Now, one of the things that I want to 34 00:01:28,050 --> 00:01:30,190 show you with this here, let's do it right here. 35 00:01:30,190 --> 00:01:32,330 Here's our main, let's start here. 36 00:01:32,330 --> 00:01:34,620 And instead of having 10 or 100,000, 37 00:01:34,620 --> 00:01:37,200 we're just gonna use 10 Go routines for a second. 38 00:01:37,200 --> 00:01:38,660 And what we're doing here-- 39 00:01:38,660 --> 00:01:40,090 oh, I gotta tell you about this device. 40 00:01:40,090 --> 00:01:43,080 So let's back up just a little bit. 41 00:01:43,080 --> 00:01:45,900 Because there's a real problem with this program. 42 00:01:45,900 --> 00:01:48,600 Remember when we said, we have an application 43 00:01:48,600 --> 00:01:52,960 that is streaming data for any particular device, 44 00:01:52,960 --> 00:01:54,580 let's say, the World Cup, since the World Cup's 45 00:01:54,580 --> 00:01:56,400 happening now, right, 46 00:01:56,400 --> 00:01:59,120 we've got hundreds of thousands of people on their phone 47 00:01:59,120 --> 00:02:01,630 in the park, watching in their office, 48 00:02:01,630 --> 00:02:02,930 watching the World Cup, 49 00:02:02,930 --> 00:02:04,900 and we got a socket connection in our server 50 00:02:04,900 --> 00:02:07,530 and we're streaming and we're logging as that happens. 51 00:02:07,530 --> 00:02:09,310 Now, here's the situation. 52 00:02:09,310 --> 00:02:12,000 Since we're letting every Go routine write the standard out 53 00:02:12,000 --> 00:02:15,080 to do their own logs, what happens if standard out 54 00:02:15,080 --> 00:02:17,410 suddenly causes blocking? 55 00:02:17,410 --> 00:02:20,780 What if every Go routine, when they attempt to write a log, 56 00:02:20,780 --> 00:02:22,890 blocks on standard out? 57 00:02:22,890 --> 00:02:24,090 You know what's gonna happen? 58 00:02:24,090 --> 00:02:27,450 Every Go routine is going to be stopped, in a waiting state. 59 00:02:27,450 --> 00:02:30,480 Basically, there is no more video streaming. 60 00:02:30,480 --> 00:02:33,280 Literally, one hundred thousands of our customers 61 00:02:33,280 --> 00:02:35,970 on this server no longer can watch the game 62 00:02:35,970 --> 00:02:38,270 and god forbid we're in a shoot out situation, 63 00:02:38,270 --> 00:02:41,490 they're really going to be upset, I would be upset. 64 00:02:41,490 --> 00:02:44,920 So let's simulate this potential problem. 65 00:02:44,920 --> 00:02:48,600 Let's simulate a situation where the logging blocks 66 00:02:48,600 --> 00:02:51,750 and it causes our entire service to deadlock 67 00:02:51,750 --> 00:02:53,300 all at one time. 68 00:02:53,300 --> 00:02:54,480 All right, so here it is. 69 00:02:54,480 --> 00:02:57,330 In order to simulate this, we gotta mock a device 70 00:02:57,330 --> 00:02:59,630 that we can cause blocking on. 71 00:02:59,630 --> 00:03:01,690 So I've defined a type called device 72 00:03:01,690 --> 00:03:04,600 and it has a field called problem 73 00:03:04,600 --> 00:03:07,380 and this is a Boolean field and we're gonna flip the bit. 74 00:03:07,380 --> 00:03:09,680 When there's no problem streaming, it's happening. 75 00:03:09,680 --> 00:03:12,620 When there is a problem, we stop all the streaming. 76 00:03:12,620 --> 00:03:14,660 The whole server comes to a halt. 77 00:03:14,660 --> 00:03:15,623 Remember something. 78 00:03:16,480 --> 00:03:19,570 You gotta decide for your server and your application, 79 00:03:19,570 --> 00:03:23,530 is it okay to stop things when we can't log? 80 00:03:23,530 --> 00:03:25,650 When it comes to streaming video, 81 00:03:25,650 --> 00:03:28,640 I know the logs are important, but you can't just stop TV 82 00:03:28,640 --> 00:03:30,360 because you can't write logs anymore. 83 00:03:30,360 --> 00:03:33,840 The TV streaming is much more important than the logging. 84 00:03:33,840 --> 00:03:37,020 That being said, we want the logging to start running again 85 00:03:37,020 --> 00:03:38,770 as soon as possible. 86 00:03:38,770 --> 00:03:41,200 We gotta detect the problem, we gotta identify it, 87 00:03:41,200 --> 00:03:43,010 we gotta fix it, we gotta keep moving. 88 00:03:43,010 --> 00:03:45,490 I've known some applications where there's so much data 89 00:03:45,490 --> 00:03:47,570 in the logs, you literally do have to stop. 90 00:03:47,570 --> 00:03:50,970 But when it comes to this video streaming, no way. 91 00:03:50,970 --> 00:03:55,260 We cannot halt this program because we can't write logs. 92 00:03:55,260 --> 00:03:56,710 So let's simulate the problem here. 93 00:03:56,710 --> 00:03:58,110 So there it is, there's the problem. 94 00:03:58,110 --> 00:04:00,280 Now, what we're doing on this device is we're implementing 95 00:04:00,280 --> 00:04:02,720 the io.Writer interface. 96 00:04:02,720 --> 00:04:04,900 Right, this way, we can allow this to be a really 97 00:04:04,900 --> 00:04:07,910 a true device and leverage the io package 98 00:04:07,910 --> 00:04:09,840 and other aspects of the standard library 99 00:04:09,840 --> 00:04:11,400 that allow for writing. 100 00:04:11,400 --> 00:04:14,670 And there we are, we implemented the io.Writer interface. 101 00:04:14,670 --> 00:04:17,610 And we look at this and if there's a problem, 102 00:04:17,610 --> 00:04:20,320 then we're in an endless loop, 103 00:04:20,320 --> 00:04:22,300 sleeping at a second at a time. 104 00:04:22,300 --> 00:04:24,360 So this is gonna simulate that disk problem. 105 00:04:24,360 --> 00:04:27,110 Right, it's gonna simulate some sort of blockage 106 00:04:27,110 --> 00:04:28,820 on trying to write to standard out. 107 00:04:28,820 --> 00:04:31,490 And if there is no problem, we break out of the loop 108 00:04:31,490 --> 00:04:35,230 and we are able to write the data, the log data, 109 00:04:35,230 --> 00:04:36,063 to the device. 110 00:04:36,063 --> 00:04:38,720 Okay, so we've simulated a mocking device here. 111 00:04:38,720 --> 00:04:39,850 So now, what do we do? 112 00:04:39,850 --> 00:04:40,690 Here we go. 113 00:04:40,690 --> 00:04:43,580 We're gonna use 10 Go routines to run this simulation. 114 00:04:43,580 --> 00:04:46,930 We create a value of our device set to its zero value 115 00:04:46,930 --> 00:04:49,740 and the standard library logger asks 116 00:04:49,740 --> 00:04:52,220 for the first parameter, any sort of concrete data 117 00:04:52,220 --> 00:04:54,240 that implements the io.Writer interface. 118 00:04:54,240 --> 00:04:56,940 Our device implements the io.Writer interface, 119 00:04:56,940 --> 00:04:58,420 so we're good to go. 120 00:04:58,420 --> 00:05:02,840 Then we launch our 10 Go routines and each Go routine here 121 00:05:02,840 --> 00:05:07,330 is writing logs to the mock device and waits 10 milliseconds 122 00:05:07,330 --> 00:05:08,500 to write to the next log. 123 00:05:08,500 --> 00:05:11,330 So we're seeing heavy logging activity 124 00:05:11,330 --> 00:05:12,383 across our 10 Go routines. 125 00:05:12,383 --> 00:05:16,180 It's streaming the World Cup to those 10 customers. 126 00:05:16,180 --> 00:05:18,360 And again, customers are gonna be upset 127 00:05:18,360 --> 00:05:20,280 if we're not able to stream. 128 00:05:20,280 --> 00:05:23,360 Now, in order to actually run the simulation, 129 00:05:23,360 --> 00:05:25,960 I bind this code into the operating system 130 00:05:25,960 --> 00:05:30,640 and I ask to get signal at any time I hit basically Ctrl-C. 131 00:05:30,640 --> 00:05:32,630 So we're in an endless loop here. 132 00:05:32,630 --> 00:05:36,440 Every time I hit Ctrl-C, we will receive on the sig channel, 133 00:05:36,440 --> 00:05:38,030 notice we create a signal channel, 134 00:05:38,030 --> 00:05:40,290 we pass it to the notify function, 135 00:05:40,290 --> 00:05:43,040 notify function binds it into the operating system, 136 00:05:43,040 --> 00:05:46,420 and now, every time we hit Ctrl-C, we'll receive. 137 00:05:46,420 --> 00:05:49,890 And every time we hit Ctrl-C, I flip the bit on the problem. 138 00:05:49,890 --> 00:05:53,000 Now, most of you are gonna recognize 139 00:05:53,000 --> 00:05:55,630 that I now have a data race here. 140 00:05:55,630 --> 00:05:57,870 This is a write 141 00:05:57,870 --> 00:06:00,210 and this is a read. 142 00:06:00,210 --> 00:06:03,430 And these read and write is not synchronized. 143 00:06:03,430 --> 00:06:07,000 There's no atomic instruction here, there's no mutex. 144 00:06:07,000 --> 00:06:10,420 You're absolutely right, this is bad production level code. 145 00:06:10,420 --> 00:06:12,310 But I don't wanna convolute the code right now 146 00:06:12,310 --> 00:06:15,260 with atomics, so let's just appreciate 147 00:06:15,260 --> 00:06:16,800 that there's a data race here 148 00:06:16,800 --> 00:06:18,750 that's not really what we're looking at. 149 00:06:18,750 --> 00:06:22,550 And we'll be able to flip this bit and we'll be fine. 150 00:06:22,550 --> 00:06:25,500 So here we are, every time I hit Ctrl-C, 151 00:06:25,500 --> 00:06:27,730 we go from no problem to problem. 152 00:06:27,730 --> 00:06:30,000 Hit Ctrl-C, problem to no problem. 153 00:06:30,000 --> 00:06:32,750 We flip it back and forth with our data race there. 154 00:06:32,750 --> 00:06:35,450 And we'll keep seeing what happens. 155 00:06:35,450 --> 00:06:38,210 Okay, great, so I've got my main application 156 00:06:38,210 --> 00:06:39,550 to run the simulation. 157 00:06:39,550 --> 00:06:44,550 So let's go now into our console, let's build this program, 158 00:06:44,630 --> 00:06:45,573 and let's run it. 159 00:06:46,560 --> 00:06:47,950 Okay. 160 00:06:47,950 --> 00:06:48,970 So here we are. 161 00:06:48,970 --> 00:06:52,590 10 devices are being streamed the World Cup. 162 00:06:52,590 --> 00:06:56,360 Now, I'm gonna count to three and hit Ctrl-C, set a problem, 163 00:06:56,360 --> 00:06:58,950 and see what happens to all of these poor people. 164 00:06:58,950 --> 00:07:01,200 One, two, three. 165 00:07:01,200 --> 00:07:02,490 Look what just happened. 166 00:07:02,490 --> 00:07:05,400 We now simulated a problem with standard out 167 00:07:05,400 --> 00:07:07,100 and the logging has now caused 168 00:07:07,100 --> 00:07:09,150 the entire server to deadlock. 169 00:07:09,150 --> 00:07:11,800 Nobody is watching the World Cup anymore. 170 00:07:11,800 --> 00:07:15,060 Now, let's say we ran out of disk space for the logs, right. 171 00:07:15,060 --> 00:07:17,700 We clear up the disk space, one, two, three. 172 00:07:17,700 --> 00:07:20,340 And boom, it starts again, but guess what? 173 00:07:20,340 --> 00:07:22,470 We run out of disk space again 174 00:07:22,470 --> 00:07:25,200 and you can see that the logging has stopped. 175 00:07:25,200 --> 00:07:27,513 This is unacceptable. 176 00:07:28,940 --> 00:07:29,773 Unacceptable. 177 00:07:29,773 --> 00:07:33,230 We can't stop streaming because we can't write logs, 178 00:07:33,230 --> 00:07:34,720 not in this case. 179 00:07:34,720 --> 00:07:38,200 So using this standard library logger 180 00:07:38,200 --> 00:07:41,210 and allowing Go routines to log to the device directly 181 00:07:41,210 --> 00:07:44,270 is not a solution we can use anymore. 182 00:07:44,270 --> 00:07:47,360 We now have to add some extra complexity 183 00:07:47,360 --> 00:07:49,810 to deal with this problem. 184 00:07:49,810 --> 00:07:52,600 Basically, we've gotta write some code now 185 00:07:52,600 --> 00:07:55,220 that lets Go routines even up to 10,000, 186 00:07:55,220 --> 00:07:57,720 write to the log, but to be able to detect 187 00:07:57,720 --> 00:07:59,800 if that log write is going to block. 188 00:07:59,800 --> 00:08:02,530 And if it is, don't let the Go routine block. 189 00:08:02,530 --> 00:08:05,430 Just bypass or skip the logs. 190 00:08:05,430 --> 00:08:08,340 Hm, how can we do this? 191 00:08:08,340 --> 00:08:11,000 Well, if we use a drop pattern. 192 00:08:11,000 --> 00:08:14,680 If we use a drop pattern, then I think we could come up 193 00:08:14,680 --> 00:08:17,340 with a very simple solution that solves the problem. 194 00:08:17,340 --> 00:08:20,230 Let's draw the drop pattern out on the board. 195 00:08:20,230 --> 00:08:21,483 So what if we did this? 196 00:08:21,483 --> 00:08:25,660 What if we create a Go routine whose entire job it is 197 00:08:25,660 --> 00:08:28,800 is to write to the device, whatever that device is? 198 00:08:28,800 --> 00:08:32,460 Right, that means that if this particular 199 00:08:32,460 --> 00:08:34,490 write to the device blocks, 200 00:08:34,490 --> 00:08:37,090 if something bad happens like we've simulated, 201 00:08:37,090 --> 00:08:40,530 well, we only have to trace or track that on one Go routine, 202 00:08:40,530 --> 00:08:43,530 not 10,000 Go routines, only on one. 203 00:08:43,530 --> 00:08:45,480 That'll make our life a lot simpler. 204 00:08:45,480 --> 00:08:48,430 So we have just one Go routine writing to the device. 205 00:08:48,430 --> 00:08:50,170 You may run into a little latency on the-- 206 00:08:50,170 --> 00:08:51,840 It's logs, it's okay. 207 00:08:51,840 --> 00:08:54,420 We can at least detect when there's a problem. 208 00:08:54,420 --> 00:08:57,680 But how do we get 10,000 Go routines, right, 209 00:08:57,680 --> 00:09:00,890 10,000 Go routines that's streaming data, 210 00:09:00,890 --> 00:09:03,350 how do we get these 10,000 Go routines 211 00:09:03,350 --> 00:09:07,490 to feed the one Go routine the log data that it needs? 212 00:09:07,490 --> 00:09:10,050 Well, we could use a channel. 213 00:09:10,050 --> 00:09:14,330 Right, we could say, these Go routines can signal 214 00:09:14,330 --> 00:09:19,330 with data, log data, that there's work for this Go routine. 215 00:09:19,600 --> 00:09:21,820 Now, if we use an on-buffer channel, 216 00:09:21,820 --> 00:09:23,610 if we try to get guarantees here, 217 00:09:23,610 --> 00:09:25,030 we're gonna be in a lot of trouble 218 00:09:25,030 --> 00:09:26,700 because then these Go routines are gonna block, 219 00:09:26,700 --> 00:09:28,920 waiting for the Go routine here to perform the receive. 220 00:09:28,920 --> 00:09:29,753 We can't. 221 00:09:29,753 --> 00:09:31,210 Latency is everything here. 222 00:09:31,210 --> 00:09:33,350 So we're gonna need a buffered channel. 223 00:09:33,350 --> 00:09:35,920 The question is, what should the size of the buffer be? 224 00:09:35,920 --> 00:09:38,100 We're definitely gonna need a buffer channel. 225 00:09:38,100 --> 00:09:39,560 Question is, what is the size? 226 00:09:39,560 --> 00:09:42,670 Well, we've gotta figure that out when we do some running 227 00:09:42,670 --> 00:09:44,930 and simulations or load testing. 228 00:09:44,930 --> 00:09:47,870 But we could say, let's create a buffer 229 00:09:47,870 --> 00:09:51,120 for every Go routine that we know is going to be existing 230 00:09:51,120 --> 00:09:54,000 in the program, maybe 10,000, in this case. 231 00:09:54,000 --> 00:09:59,000 It has to be measurable and we also have to make sure that 232 00:09:59,490 --> 00:10:01,320 it's a reasonable number. 233 00:10:01,320 --> 00:10:03,180 Now, why does this work for us? 234 00:10:03,180 --> 00:10:04,880 Why does the buffer pattern work for us? 235 00:10:04,880 --> 00:10:07,070 Again, we use a drop pattern. 236 00:10:07,070 --> 00:10:09,440 If we use a drop pattern, then life is amazing here. 237 00:10:09,440 --> 00:10:10,480 Think about it. 238 00:10:10,480 --> 00:10:12,370 While there's room in the buffer, 239 00:10:12,370 --> 00:10:15,520 these Go routines can perform their send 240 00:10:15,520 --> 00:10:18,680 and there's no latency between the send and the receive. 241 00:10:18,680 --> 00:10:21,440 There will be some latency between multiple sends, 242 00:10:21,440 --> 00:10:22,910 but that should be quick. 243 00:10:22,910 --> 00:10:26,150 And that means if there's room in the buffer, 244 00:10:26,150 --> 00:10:28,320 that means that we are good, right. 245 00:10:28,320 --> 00:10:30,070 The system is healthy. 246 00:10:30,070 --> 00:10:32,240 Even if we're maintaining a little bit here, 247 00:10:32,240 --> 00:10:34,320 this is gonna show health 248 00:10:34,320 --> 00:10:36,300 and all the logs are gonna get done. 249 00:10:36,300 --> 00:10:38,250 But what happens if this blocks? 250 00:10:38,250 --> 00:10:41,030 If this blocks, then this buffer's gonna get full. 251 00:10:41,030 --> 00:10:43,380 In fact, it's gonna get full very, very quickly. 252 00:10:43,380 --> 00:10:45,570 And what the drop pattern's gonna say is, 253 00:10:45,570 --> 00:10:48,660 attempt to signal with data on this buffer channel. 254 00:10:48,660 --> 00:10:49,810 And if you can, great. 255 00:10:49,810 --> 00:10:54,230 If not, we are not going to wait, we are not going to block. 256 00:10:54,230 --> 00:10:56,570 What we're gonna do then is 257 00:10:56,570 --> 00:10:58,220 drop, 258 00:10:58,220 --> 00:10:59,820 drop. 259 00:10:59,820 --> 00:11:04,230 We're basically gonna drop that log line and not log it, 260 00:11:04,230 --> 00:11:06,830 but we're not gonna stop the video stream. 261 00:11:06,830 --> 00:11:07,663 Think about this. 262 00:11:07,663 --> 00:11:10,360 This buffer channel is gonna give us the ability to detect 263 00:11:10,360 --> 00:11:12,430 when we're not able to write to the device. 264 00:11:12,430 --> 00:11:14,720 But once the device is writing again, 265 00:11:14,720 --> 00:11:17,240 this buffer will clear pretty quickly 266 00:11:17,240 --> 00:11:20,210 and we'll be able to recover. 267 00:11:20,210 --> 00:11:23,090 This is brilliant, so let's use a drop pattern 268 00:11:23,090 --> 00:11:27,070 to solve this problem, okay, and get this code back 269 00:11:27,070 --> 00:11:30,500 into production quickly without this fear that our customers 270 00:11:30,500 --> 00:11:33,050 are gonna lose their ability to watch the game. 271 00:11:33,050 --> 00:11:34,850 So let's write this package. 272 00:11:34,850 --> 00:11:36,920 I've added a folder called logger 273 00:11:36,920 --> 00:11:38,780 and I've got a logger.go 274 00:11:38,780 --> 00:11:41,370 and we've got the package logger in here. 275 00:11:41,370 --> 00:11:44,690 Now, what we need is a logger type. 276 00:11:44,690 --> 00:11:49,090 So let's go ahead and create a logger type using the struct. 277 00:11:49,090 --> 00:11:52,400 And let's go ahead, because my lint is gonna complain, 278 00:11:52,400 --> 00:11:54,133 we're gonna have to have some comments here. 279 00:11:54,133 --> 00:11:55,640 My lint is gonna complain 280 00:11:55,640 --> 00:11:58,330 because comments on exported things 281 00:11:58,330 --> 00:12:00,650 really become part of the Go documentation. 282 00:12:00,650 --> 00:12:03,760 I'll show you some of that later on the class. 283 00:12:03,760 --> 00:12:05,870 But I don't want all the squiggly lines right now, 284 00:12:05,870 --> 00:12:08,460 so we're just gonna add some comments like this. 285 00:12:08,460 --> 00:12:11,060 Now, there's two core data structures here that we need. 286 00:12:11,060 --> 00:12:13,930 We need this Go routine and we need this buffer. 287 00:12:13,930 --> 00:12:15,360 We need the Go routine that can perform 288 00:12:15,360 --> 00:12:16,207 writes to the device. 289 00:12:16,207 --> 00:12:18,850 And we need a buffer that the rest of the Go routines 290 00:12:18,850 --> 00:12:20,540 are gonna signal into. 291 00:12:20,540 --> 00:12:23,680 So let's start with the channel 292 00:12:23,680 --> 00:12:26,190 and let's just start with the channel type string. 293 00:12:26,190 --> 00:12:28,030 Right now, it says, log data is string data. 294 00:12:28,030 --> 00:12:30,780 We can come up with a simple solution here. 295 00:12:30,780 --> 00:12:33,060 And since we're gonna have a Go routine, 296 00:12:33,060 --> 00:12:36,316 I'm also going to need a WaitGroup. 297 00:12:36,316 --> 00:12:38,020 And the WaitGroup is gonna be important 298 00:12:38,020 --> 00:12:40,300 because it's gonna let us manage the concurrency 299 00:12:40,300 --> 00:12:42,120 of the lifecycle of this Go routine. 300 00:12:42,120 --> 00:12:44,480 We gotta give the caller the ability 301 00:12:44,480 --> 00:12:45,890 to not just start this Go routine, 302 00:12:45,890 --> 00:12:47,720 but to terminate it as well. 303 00:12:47,720 --> 00:12:49,410 Remember, you can't create a Go routine unless you know 304 00:12:49,410 --> 00:12:51,510 how and when it's going to terminate. 305 00:12:51,510 --> 00:12:54,660 So that's pretty good, I think that's all we need right now. 306 00:12:54,660 --> 00:12:56,590 And any time we're finished with the type, 307 00:12:56,590 --> 00:12:58,770 we're gonna need a factory function. 308 00:12:58,770 --> 00:13:01,050 So our factory function, as we know, 309 00:13:01,050 --> 00:13:02,830 is gonna return a logger. 310 00:13:02,830 --> 00:13:05,070 We should be using pointer semantics 311 00:13:05,070 --> 00:13:07,420 when you can't make copies of loggers anymore 312 00:13:07,420 --> 00:13:08,810 because we can't make a copy of the WaitGroup, 313 00:13:08,810 --> 00:13:11,080 that would create a different WaitGroup. 314 00:13:11,080 --> 00:13:12,720 Plus, there's only one logger. 315 00:13:12,720 --> 00:13:13,780 We only want one logger. 316 00:13:13,780 --> 00:13:15,550 We don't wanna make copies of logger. 317 00:13:15,550 --> 00:13:17,570 So pointer semantics are good. 318 00:13:17,570 --> 00:13:20,220 And the very first thing that we're gonna wanna do here 319 00:13:20,220 --> 00:13:22,900 is create a logger, but I need some things first. 320 00:13:22,900 --> 00:13:26,310 First thing I need to know is what device... 321 00:13:27,450 --> 00:13:29,480 what device we wanna write to. 322 00:13:29,480 --> 00:13:31,590 So we'll accept the device, the concrete data 323 00:13:31,590 --> 00:13:33,830 through the io.Writer interface. 324 00:13:33,830 --> 00:13:35,830 And then we need to know what the capacity 325 00:13:35,830 --> 00:13:37,780 of our buffer is going to be. 326 00:13:37,780 --> 00:13:39,460 And only the caller can tell us 327 00:13:39,460 --> 00:13:42,380 'cause only the caller knows its application. 328 00:13:42,380 --> 00:13:46,900 So now, we can go ahead and create a logger, there it is. 329 00:13:46,900 --> 00:13:50,210 And the WaitGroup's already usable in its zero value state, 330 00:13:50,210 --> 00:13:51,460 so we're good there. 331 00:13:51,460 --> 00:13:54,370 But we do need to make the channel, 332 00:13:54,370 --> 00:13:57,530 so let's go ahead and make that string channel 333 00:13:57,530 --> 00:14:00,210 and use the capacity 334 00:14:00,210 --> 00:14:01,460 as the buffer. 335 00:14:01,460 --> 00:14:03,090 Now, we get the squiggly lines 336 00:14:03,090 --> 00:14:05,580 because we haven't used a variable yet. 337 00:14:05,580 --> 00:14:09,140 So we could return the address of l. 338 00:14:09,140 --> 00:14:13,070 Notice again that I'm using value semantics on construction 339 00:14:13,070 --> 00:14:15,110 because we're assigning it to a variable. 340 00:14:15,110 --> 00:14:17,410 We wanna return the address out like that 341 00:14:17,410 --> 00:14:18,450 because it's more readable. 342 00:14:18,450 --> 00:14:20,610 We now know we have a potential allocation 343 00:14:20,610 --> 00:14:21,660 because of line 20. 344 00:14:21,660 --> 00:14:23,810 We're really creating readability around 345 00:14:23,810 --> 00:14:25,040 escape analysis here. 346 00:14:25,040 --> 00:14:27,640 Great, I've got the logger, that's okay. 347 00:14:27,640 --> 00:14:29,700 But now, we gotta go create the Go routine. 348 00:14:29,700 --> 00:14:33,660 So we know that we need a Go routine whose entire job it is 349 00:14:33,660 --> 00:14:35,640 is to perform these writes. 350 00:14:35,640 --> 00:14:40,130 Okay, great, so how can we go these writes here 351 00:14:40,130 --> 00:14:41,950 on the Go routine when we're gonna have to receive 352 00:14:41,950 --> 00:14:43,060 off the channel? 353 00:14:43,060 --> 00:14:44,960 So what if we did the following? 354 00:14:44,960 --> 00:14:48,900 What if we received to that logging data 355 00:14:48,900 --> 00:14:50,740 using our for range 356 00:14:50,740 --> 00:14:55,740 and we'll leverage our closures to keep this code clean. 357 00:14:55,980 --> 00:14:57,750 Look at what we've done here now. 358 00:14:57,750 --> 00:15:00,330 What we're seeing is this Go routine is going to receive 359 00:15:00,330 --> 00:15:03,440 off the buffer channel and then what it has to do 360 00:15:03,440 --> 00:15:07,160 is write that to the device that we've asked it to, 361 00:15:07,160 --> 00:15:08,480 that we've passed in. 362 00:15:08,480 --> 00:15:11,463 What's great is the fmt package has an Fprint 363 00:15:13,210 --> 00:15:14,470 function here 364 00:15:14,470 --> 00:15:18,700 and the Fprint function takes as its first parameter... 365 00:15:18,700 --> 00:15:19,910 Let's see here. 366 00:15:19,910 --> 00:15:22,450 Let's just write fmt 'cause off the top of my head, 367 00:15:22,450 --> 00:15:25,966 I'm missing information about this function, fmt. 368 00:15:25,966 --> 00:15:27,490 Fprint. 369 00:15:27,490 --> 00:15:29,400 Yup, I was right, Fprint, that's all we need. 370 00:15:29,400 --> 00:15:33,590 And all we can do then is pass it w for the writer 371 00:15:33,590 --> 00:15:35,560 or the device, and v is the value. 372 00:15:35,560 --> 00:15:37,150 So look at what we've done here. 373 00:15:37,150 --> 00:15:39,700 We've said, okay, we're gonna write whatever data 374 00:15:39,700 --> 00:15:43,743 we receive off the channel to the device that we passed 375 00:15:43,743 --> 00:15:46,950 that will do it through the fmt.Fprint function. 376 00:15:46,950 --> 00:15:47,783 This is pretty cool. 377 00:15:47,783 --> 00:15:50,470 I mean, this Go routine now is able to write to device, 378 00:15:50,470 --> 00:15:53,000 format it, writes to the device, but we're not done yet. 379 00:15:53,000 --> 00:15:56,930 We do not have our full orchestration in place. 380 00:15:56,930 --> 00:15:58,920 So let's do the following. 381 00:15:58,920 --> 00:16:01,960 How about WaitGroup add one? 382 00:16:01,960 --> 00:16:06,630 And when this Go routine is finally terminated, 383 00:16:06,630 --> 00:16:09,100 then l.wg.done. 384 00:16:09,100 --> 00:16:10,710 So look at what we've done here. 385 00:16:10,710 --> 00:16:13,300 We've set up that there's gonna be one Go routine 386 00:16:13,300 --> 00:16:15,200 that this package creates. 387 00:16:15,200 --> 00:16:18,380 And once we are able to terminate this Go routine, 388 00:16:18,380 --> 00:16:20,460 we'll decrement the WaitGroup to zero. 389 00:16:20,460 --> 00:16:22,530 Anytime you create a Go routine, you have to provide 390 00:16:22,530 --> 00:16:25,170 a mechanism for being able to terminate it 391 00:16:25,170 --> 00:16:27,300 especially if it's part of an API for the caller. 392 00:16:27,300 --> 00:16:29,420 Let them have the ability to manage concurrency 393 00:16:29,420 --> 00:16:30,400 all the way down. 394 00:16:30,400 --> 00:16:33,040 Which now brings us up to our next method 395 00:16:33,040 --> 00:16:34,550 because this function's done. 396 00:16:34,550 --> 00:16:35,640 We've created a logger. 397 00:16:35,640 --> 00:16:37,720 We've initialized the buffer channel. 398 00:16:37,720 --> 00:16:39,060 We've got our WaitGroup one. 399 00:16:39,060 --> 00:16:41,260 We create the Go routine that knows how to write, 400 00:16:41,260 --> 00:16:44,350 where back here, able to write to the device. 401 00:16:44,350 --> 00:16:48,530 Then we defer the done, we range over the channel, 402 00:16:48,530 --> 00:16:50,400 getting data, and we write it. 403 00:16:50,400 --> 00:16:52,980 Okay, brilliant, then using our pointer semantics there 404 00:16:52,980 --> 00:16:54,240 to return it back out. 405 00:16:54,240 --> 00:16:57,090 Okay, great, now, let's use a method 406 00:16:57,090 --> 00:16:58,260 'cause we're gonna have to be able to try 407 00:16:58,260 --> 00:17:00,200 to close this logger. 408 00:17:00,200 --> 00:17:03,250 So we'll do this, maybe we'll just call it close. 409 00:17:03,250 --> 00:17:07,200 Again, I want my comments, so let's just do this. 410 00:17:07,200 --> 00:17:10,040 And what close is gonna do 411 00:17:10,040 --> 00:17:12,870 is give our API the ability to shut down this Go routine 412 00:17:12,870 --> 00:17:14,380 in order of fashion. 413 00:17:14,380 --> 00:17:17,040 So in order to shut down the Go routine, 414 00:17:17,040 --> 00:17:19,430 what we're gonna have to do is close the channel. 415 00:17:19,430 --> 00:17:24,220 That will terminate the for loop, which will then call done. 416 00:17:24,220 --> 00:17:26,733 So then all we have to do is wg.Wait. 417 00:17:27,750 --> 00:17:30,480 Look at what we've done, nothing more than that. 418 00:17:30,480 --> 00:17:34,740 Close the channel causes the for loop to terminate, 419 00:17:34,740 --> 00:17:38,560 then we wait 'til it reports on the done, and we're good. 420 00:17:38,560 --> 00:17:41,170 Now, the person using this API has to make sure 421 00:17:41,170 --> 00:17:44,340 they do not close the logger 'til every Go routine 422 00:17:44,340 --> 00:17:46,840 that can potentially log is finished. 423 00:17:46,840 --> 00:17:47,673 But guess what? 424 00:17:47,673 --> 00:17:49,720 I want a clean shutdown of my software 425 00:17:49,720 --> 00:17:51,960 and we should be able to make that happen. 426 00:17:51,960 --> 00:17:55,840 Now, I need one more API to get this finally finished 427 00:17:55,840 --> 00:17:58,870 and that's this call here to print. 428 00:17:58,870 --> 00:18:01,670 This is really what was causing us pain before. 429 00:18:01,670 --> 00:18:03,820 Right, the print call was blocking, 430 00:18:03,820 --> 00:18:04,850 which was causing us our pain. 431 00:18:04,850 --> 00:18:07,120 We can't let the print call block. 432 00:18:07,120 --> 00:18:10,880 So we're gonna have to implement this 433 00:18:10,880 --> 00:18:13,990 as a method again off of our logger. 434 00:18:13,990 --> 00:18:17,360 We'll use our pointer semantics, we'll call it print. 435 00:18:17,360 --> 00:18:18,860 We'll keep it simple 436 00:18:18,860 --> 00:18:21,340 and we'll just ask for a string right now. 437 00:18:21,340 --> 00:18:23,680 We can always make this more complex later. 438 00:18:23,680 --> 00:18:28,080 Not important, but at least the print call here 439 00:18:28,080 --> 00:18:30,460 can mock the print call here. 440 00:18:30,460 --> 00:18:32,460 Now, what does print have to do? 441 00:18:32,460 --> 00:18:36,450 It's got to signal with data, the data we want to log, 442 00:18:36,450 --> 00:18:40,070 but it's gotta be able to identify if this buffer is full, 443 00:18:40,070 --> 00:18:42,410 to drop the log and not wait. 444 00:18:42,410 --> 00:18:43,260 Guess what? 445 00:18:43,260 --> 00:18:45,740 We get to use a select statement. 446 00:18:45,740 --> 00:18:48,270 And what we can first say is, case. 447 00:18:48,270 --> 00:18:50,770 Okay, case l.ch, 448 00:18:50,770 --> 00:18:53,590 signal with data v. 449 00:18:53,590 --> 00:18:56,800 If this signaling with data succeeds, 450 00:18:56,800 --> 00:18:58,360 it means it got into the buffer, 451 00:18:58,360 --> 00:19:00,140 it means this Go routine over here. 452 00:19:00,140 --> 00:19:02,190 If on line 41, 453 00:19:02,190 --> 00:19:04,570 the signal right now... 454 00:19:04,570 --> 00:19:07,770 sending with data succeeds, it got into the buffer, 455 00:19:07,770 --> 00:19:10,830 this Go routine will process it. 456 00:19:10,830 --> 00:19:13,427 Or we go to the default case. 457 00:19:13,427 --> 00:19:17,190 And the default case says, if line 41's going to block, 458 00:19:17,190 --> 00:19:20,500 there's no room in the buffer, then we wanna drop. 459 00:19:20,500 --> 00:19:23,690 In this case, what I'm gonna do is write the word drop 460 00:19:23,690 --> 00:19:26,660 to the screen, so we can see 461 00:19:26,660 --> 00:19:30,130 that we're no longer blocking like we did before. 462 00:19:30,130 --> 00:19:31,720 We've moved into the default case. 463 00:19:31,720 --> 00:19:33,170 We're dropping these logs. 464 00:19:33,170 --> 00:19:35,590 We're basically still running and streaming data. 465 00:19:35,590 --> 00:19:39,860 Look, after 49 lines of code, I am done with an API 466 00:19:39,860 --> 00:19:41,270 that should solve our problem 467 00:19:41,270 --> 00:19:45,470 and the only level of complexity is a buffer channel 468 00:19:45,470 --> 00:19:47,170 and one Go routine. 469 00:19:47,170 --> 00:19:51,600 So let's bring this logger that we just wrote into the app 470 00:19:51,600 --> 00:19:53,500 and see how it works. 471 00:19:53,500 --> 00:19:56,670 So we've gotta first import it in. 472 00:19:56,670 --> 00:20:00,320 So what I'm gonna do is I took the path of this 473 00:20:00,320 --> 00:20:03,280 and I'm gonna get it to our relative Go path. 474 00:20:03,280 --> 00:20:04,490 And another thing I'm gonna do 475 00:20:04,490 --> 00:20:08,160 just to keep this really clean is I'm going to rename 476 00:20:08,160 --> 00:20:10,860 the logger namespace to log 477 00:20:10,860 --> 00:20:14,560 and that means that I can just make a small few changes 478 00:20:14,560 --> 00:20:16,230 to our application. 479 00:20:16,230 --> 00:20:18,590 Okay, brilliant, so there it is. 480 00:20:18,590 --> 00:20:19,510 Now, 481 00:20:19,510 --> 00:20:23,550 since the logger has the same API, 482 00:20:23,550 --> 00:20:27,350 really, the only thing we have to do is replace the 483 00:20:27,350 --> 00:20:29,300 construction 484 00:20:29,300 --> 00:20:32,350 of the logger by using our new function, 485 00:20:32,350 --> 00:20:34,170 our new new function, right. 486 00:20:34,170 --> 00:20:38,840 And we have the device and that device is the address of d. 487 00:20:38,840 --> 00:20:41,950 And then we want the capacity, which will start off 488 00:20:41,950 --> 00:20:44,120 with the number of Go routines. 489 00:20:44,120 --> 00:20:48,823 Now, we might find that we need more or less capacity. 490 00:20:48,823 --> 00:20:51,720 What we don't want is false positives, 491 00:20:51,720 --> 00:20:53,470 where we're really not in trouble, 492 00:20:53,470 --> 00:20:56,210 but we filled up the buffer a little too fast 493 00:20:56,210 --> 00:20:58,380 and then now, we think that we're in trouble. 494 00:20:58,380 --> 00:20:59,910 So we've gotta find that number. 495 00:20:59,910 --> 00:21:01,790 We can only do that during load testing. 496 00:21:01,790 --> 00:21:03,890 But starting with at least the number of Go routines 497 00:21:03,890 --> 00:21:05,890 should give us a good starting point. 498 00:21:05,890 --> 00:21:06,940 Look what I've done. 499 00:21:06,940 --> 00:21:09,650 We're now using our new logger API. 500 00:21:09,650 --> 00:21:12,867 I've changed the factory function now to use the same device 501 00:21:12,867 --> 00:21:14,230 and the number of Go routines. 502 00:21:14,230 --> 00:21:17,330 And since we've already implemented the same API, 503 00:21:17,330 --> 00:21:18,230 we're done. 504 00:21:18,230 --> 00:21:20,550 All I had to do is import our logger, 505 00:21:20,550 --> 00:21:24,390 reuse the log namespace, use the new factory function, 506 00:21:24,390 --> 00:21:25,330 and we're in. 507 00:21:25,330 --> 00:21:26,390 Now, I'm kinda scared here. 508 00:21:26,390 --> 00:21:29,000 Let's see if we actually did this right. 509 00:21:29,000 --> 00:21:30,510 So let's run the program now. 510 00:21:30,510 --> 00:21:34,320 We're gonna switch over to that console window and run it. 511 00:21:34,320 --> 00:21:36,890 But I wanna double-check the code real quick 512 00:21:36,890 --> 00:21:39,420 to make sure that everything is going to be perfect 513 00:21:39,420 --> 00:21:40,890 when I run this program. 514 00:21:40,890 --> 00:21:43,660 And when I come back and I look at the logger, 515 00:21:43,660 --> 00:21:48,110 I realize that we're not adding extra line feeds 516 00:21:48,110 --> 00:21:49,520 to the output, 517 00:21:49,520 --> 00:21:52,120 which is gonna make the output look a little silly. 518 00:21:52,120 --> 00:21:54,350 So I'm gonna change our package real quick 519 00:21:54,350 --> 00:21:56,040 to use those line feeds. 520 00:21:56,040 --> 00:21:57,500 We're gonna come back in here again. 521 00:21:57,500 --> 00:21:59,540 We've got the new logger in place. 522 00:21:59,540 --> 00:22:02,050 And now, I'm gonna build this program 523 00:22:02,050 --> 00:22:04,640 and we're gonna start running it. 524 00:22:04,640 --> 00:22:09,160 Now, we can see like we saw before, we are logging data. 525 00:22:09,160 --> 00:22:10,580 We're logging, we're streaming video, 526 00:22:10,580 --> 00:22:12,270 everybody's watching the World Cup. 527 00:22:12,270 --> 00:22:14,480 I'm now gonna simulate that disk problem. 528 00:22:14,480 --> 00:22:17,110 One, two, three, bam! 529 00:22:17,110 --> 00:22:19,630 Look what happened, we got the drop. 530 00:22:19,630 --> 00:22:22,120 We're now in the select. 531 00:22:22,120 --> 00:22:25,040 Right, we're now in here. 532 00:22:25,040 --> 00:22:28,130 We are still streaming video, 533 00:22:28,130 --> 00:22:31,180 right, unlike before when we were completely blocked. 534 00:22:31,180 --> 00:22:34,690 Now, let's fix the problem, maybe we ran out of disk space. 535 00:22:34,690 --> 00:22:36,150 One, two, three. 536 00:22:36,150 --> 00:22:36,990 Guess what? 537 00:22:36,990 --> 00:22:39,430 We've identified that the problem has been fixed 538 00:22:39,430 --> 00:22:41,050 and now, we're logging again. 539 00:22:41,050 --> 00:22:43,470 This is it, this is engineering. 540 00:22:43,470 --> 00:22:46,090 This is the beautiful pieces of that 541 00:22:46,090 --> 00:22:49,000 idea that channels give us orchestration, 542 00:22:49,000 --> 00:22:53,090 signaling semantics that can really help us 543 00:22:53,090 --> 00:22:57,140 identify problems quickly and deal with problems 544 00:22:57,140 --> 00:23:02,070 and then recover without any extra levels of complexity. 545 00:23:02,070 --> 00:23:04,500 Again, 45 lines of code 546 00:23:04,500 --> 00:23:08,410 gave us the ability now to detect a problem, 547 00:23:08,410 --> 00:23:10,120 recover from that problem, 548 00:23:10,120 --> 00:23:12,770 and keep our customers happy because they're still 549 00:23:14,314 --> 00:23:16,853 able to stream video to their devices.