1 00:00:06,363 --> 00:00:08,490 - So we've gone through the cache coherency, 2 00:00:08,490 --> 00:00:10,480 file sharing issues, I've shown you how to do 3 00:00:10,480 --> 00:00:12,190 synchronization with both 4 00:00:12,190 --> 00:00:14,550 atomic instructions and mutexes. 5 00:00:14,550 --> 00:00:16,390 I wanna show you something that's incredibly cool, 6 00:00:16,390 --> 00:00:18,660 which is Go's race detector. 7 00:00:18,660 --> 00:00:19,610 This has been in the language, 8 00:00:19,610 --> 00:00:21,360 I think it's been around since 1.3. 9 00:00:23,187 --> 00:00:25,120 What's really brilliant about the race detector 10 00:00:25,120 --> 00:00:27,420 is that when it was introduced, they ran it 11 00:00:27,420 --> 00:00:28,990 against the standard library 12 00:00:28,990 --> 00:00:31,460 and they found races in the standard library. 13 00:00:31,460 --> 00:00:34,220 I love when the Go language team creates tooling 14 00:00:34,220 --> 00:00:36,360 and they find their own problems. 15 00:00:36,360 --> 00:00:38,410 It was really brilliant stuff. 16 00:00:38,410 --> 00:00:41,480 The race detector's built into the Go front end, 17 00:00:41,480 --> 00:00:43,840 so when we do go build, it's built into go build. 18 00:00:43,840 --> 00:00:45,860 It's also built into the go test. 19 00:00:45,860 --> 00:00:48,000 I tend to use the race detector more on 20 00:00:48,000 --> 00:00:50,050 go test than go build. 21 00:00:50,050 --> 00:00:52,020 But I wanna show you how cool this is 22 00:00:52,020 --> 00:00:55,140 because finding data races can be impossible. 23 00:00:55,140 --> 00:00:56,940 I've been in shops before where it's taken like 24 00:00:56,940 --> 00:00:59,230 three years to find data races, 25 00:00:59,230 --> 00:01:00,180 and now with this tooling, 26 00:01:00,180 --> 00:01:02,360 we can find it very quickly. 27 00:01:02,360 --> 00:01:05,630 Let's go back to the original program we had 28 00:01:05,630 --> 00:01:09,000 where we didn't make these three lines of code atomic. 29 00:01:09,000 --> 00:01:11,010 Remember we've got the two goroutines. 30 00:01:11,010 --> 00:01:12,130 Each goroutine's incrementing 31 00:01:12,130 --> 00:01:15,200 that global variable twice; there it is. 32 00:01:15,200 --> 00:01:16,780 Remember what happened when I put 33 00:01:16,780 --> 00:01:18,930 the (mumbles) print statement in here, 34 00:01:18,930 --> 00:01:20,550 then the race showed itself 35 00:01:20,550 --> 00:01:22,600 just because of the output. 36 00:01:22,600 --> 00:01:25,080 But if I come back here and build this code again, 37 00:01:25,080 --> 00:01:26,980 go build, then we run it ... 38 00:01:26,980 --> 00:01:29,410 Remember we're still getting the right answer of four. 39 00:01:29,410 --> 00:01:31,590 There it is; four, four, four, four. 40 00:01:31,590 --> 00:01:34,390 We don't get any visual clue that there's 41 00:01:34,390 --> 00:01:37,080 a problem with this code, that things are not 42 00:01:37,080 --> 00:01:39,770 atomic until I cause some sort of 43 00:01:39,770 --> 00:01:41,910 synchronization issue right here. 44 00:01:41,910 --> 00:01:44,350 So we do build again, I run it. 45 00:01:44,350 --> 00:01:45,560 Now we're getting that, too. 46 00:01:45,560 --> 00:01:47,860 Remember that from the first example. 47 00:01:47,860 --> 00:01:49,860 But this is the cool part. 48 00:01:49,860 --> 00:01:54,260 If I just do the following, go build -race, 49 00:01:54,260 --> 00:01:55,790 then what's gonna happen is this code's gonna be 50 00:01:55,790 --> 00:01:57,250 built with the race detector. 51 00:01:57,250 --> 00:02:01,000 This code's gonna run probably 25 to 30% slower, 52 00:02:01,000 --> 00:02:02,750 but now what happens when I run it? 53 00:02:04,010 --> 00:02:05,570 The program stops. 54 00:02:05,570 --> 00:02:07,450 We get a data race stack trace 55 00:02:07,450 --> 00:02:09,860 and now we know exactly what's going on. 56 00:02:09,860 --> 00:02:11,940 You can see the stack trace said that 57 00:02:11,940 --> 00:02:15,760 you launched a goroutine on line 29; 58 00:02:15,760 --> 00:02:18,330 goroutine 6 on line 29. 59 00:02:18,330 --> 00:02:22,540 And then goroutine 7 had a read 60 00:02:22,540 --> 00:02:24,770 while there was also a write going on 61 00:02:24,770 --> 00:02:27,600 by the other goroutines, line 33 and 39. 62 00:02:27,600 --> 00:02:30,330 In other words, we had a un-synchronized 63 00:02:30,330 --> 00:02:33,970 read and write between line 33 and 39. 64 00:02:33,970 --> 00:02:36,880 If we look at 33 and 39, there's our read, 65 00:02:36,880 --> 00:02:38,420 then there's our write. 66 00:02:38,420 --> 00:02:41,360 So yeah, there it is; the race detector identify 67 00:02:41,360 --> 00:02:43,800 that we're going to have data corruption, 68 00:02:43,800 --> 00:02:45,780 and it found it for us. 69 00:02:45,780 --> 00:02:48,560 If you get a race detection like this 70 00:02:48,560 --> 00:02:52,360 and it's not obvious why that race exists, 71 00:02:52,360 --> 00:02:54,340 then I need you to cause pausing. 72 00:02:54,340 --> 00:02:56,870 Because once the race detector finds something, 73 00:02:56,870 --> 00:02:59,070 it should be obvious to you, if you have a good, 74 00:02:59,070 --> 00:03:00,320 strong, mental model of the code 75 00:03:00,320 --> 00:03:02,820 and it's not too clever, how two goroutines could've 76 00:03:02,820 --> 00:03:05,320 came to that point at the same time. 77 00:03:05,320 --> 00:03:06,830 But I've written code before that's gotten 78 00:03:06,830 --> 00:03:09,630 so out of hand and complex on concurrency 79 00:03:09,630 --> 00:03:12,000 that the race detector fires and I'm like, 80 00:03:12,000 --> 00:03:14,330 "Oh my God, how did I get there?" 81 00:03:14,330 --> 00:03:15,910 Usually that means that we're not gonna try to 82 00:03:15,910 --> 00:03:18,830 fix it with a Band-Aid or just throw a mutex at it. 83 00:03:18,830 --> 00:03:21,340 We've gotta probably refactor 84 00:03:21,340 --> 00:03:23,670 some of that code to simplify things. 85 00:03:23,670 --> 00:03:26,570 Even when they ran this thing as a standard library, 86 00:03:26,570 --> 00:03:28,580 they found some interesting code that wasn't 87 00:03:28,580 --> 00:03:32,710 obvious to anybody on how that data race was there. 88 00:03:32,710 --> 00:03:34,490 This is really brilliant stuff. 89 00:03:34,490 --> 00:03:37,940 Most of the time, I run the race detector on go test. 90 00:03:37,940 --> 00:03:42,090 I'll say go test -race for those tests that I have 91 00:03:42,090 --> 00:03:43,760 that are gonna be running in parallel ... 92 00:03:43,760 --> 00:03:45,680 Where we're gonna have multiple goroutines. 93 00:03:45,680 --> 00:03:47,250 That's where I tend to run it. 94 00:03:47,250 --> 00:03:50,410 I have heard of companies that run on a Web farm 95 00:03:50,410 --> 00:03:52,640 where you have multiple instances of your service. 96 00:03:52,640 --> 00:03:55,610 They might run one instance of a binary 97 00:03:55,610 --> 00:03:57,670 with the race detector on. 98 00:03:57,670 --> 00:04:00,170 The general rule is this; if the race detector 99 00:04:00,170 --> 00:04:01,940 finds a race, guess what? 100 00:04:01,940 --> 00:04:03,930 You have a race; fix it. 101 00:04:03,930 --> 00:04:05,720 You've got at least two goroutines 102 00:04:05,720 --> 00:04:07,290 where one's doing a read and one's doing a write 103 00:04:07,290 --> 00:04:10,420 to the same memory location at the same time. 104 00:04:10,420 --> 00:04:13,190 But if the race detector doesn't find a race, 105 00:04:13,190 --> 00:04:14,760 it doesn't mean you don't have one, 106 00:04:14,760 --> 00:04:16,850 it just hasn't found it yet. (laughs) 107 00:04:16,850 --> 00:04:19,750 I've been in environments where, on my Mac, 108 00:04:19,750 --> 00:04:21,610 the race detector doesn't find anything 109 00:04:21,610 --> 00:04:23,710 because the Scheduler isn't creating enough, 110 00:04:23,710 --> 00:04:26,550 let's say, wacky concurrency or context switches. 111 00:04:26,550 --> 00:04:30,350 And then the code moves to CI on some Linux platform 112 00:04:30,350 --> 00:04:32,980 and CI keeps failing on the race detection. 113 00:04:32,980 --> 00:04:34,650 You really should be running your tests 114 00:04:34,650 --> 00:04:36,150 with the race detector on, 115 00:04:36,150 --> 00:04:38,640 especially if you're writing multi-threaded software. 116 00:04:39,890 --> 00:04:41,900 This race detector's very, very powerful. 117 00:04:41,900 --> 00:04:44,950 You've got to make sure you're using it 118 00:04:44,950 --> 00:04:47,450 when you're writing multi-threaded software in Go.