WEBVTT

1
00:00:00.080 --> 00:00:03.799
<v Speaker 1>You know, I want you to imagine walking into your

2
00:00:03.799 --> 00:00:06.719
<v Speaker 1>absolute favorite coffee shop. You go to the exact same

3
00:00:06.719 --> 00:00:08.400
<v Speaker 1>barista every single day, Right.

4
00:00:08.359 --> 00:00:10.679
<v Speaker 2>They know your order by heart exactly.

5
00:00:10.320 --> 00:00:12.720
<v Speaker 1>Or at least they should. But imagine every time you

6
00:00:12.720 --> 00:00:14.800
<v Speaker 1>step up to the counter they just stare at you

7
00:00:15.320 --> 00:00:18.879
<v Speaker 1>with this totally blank, uncomprehending expression.

8
00:00:18.960 --> 00:00:21.239
<v Speaker 2>Oh, that would be incredibly frustrating.

9
00:00:21.359 --> 00:00:23.920
<v Speaker 1>It would. They demand to see your ID, They pull

10
00:00:24.000 --> 00:00:28.079
<v Speaker 1>up this massive, heavy filing cabinet, verify all your past orders,

11
00:00:28.120 --> 00:00:31.839
<v Speaker 1>cross reference your credit history, and only then, like after

12
00:00:31.920 --> 00:00:34.119
<v Speaker 1>all of that, do they suddenly remember that you just

13
00:00:34.159 --> 00:00:36.079
<v Speaker 1>want your usual large flat white.

14
00:00:36.200 --> 00:00:39.359
<v Speaker 3>It's exhausting just thinking about it, honestly. But you know,

15
00:00:39.399 --> 00:00:41.840
<v Speaker 3>if we connect this to the bigger picture, what you

16
00:00:41.880 --> 00:00:44.679
<v Speaker 3>are describing is exactly how the modern web operates. Yeah,

17
00:00:44.840 --> 00:00:48.799
<v Speaker 3>we've basically built an entire global infrastructure on this massive

18
00:00:48.840 --> 00:00:50.520
<v Speaker 3>illusion called statelessness.

19
00:00:50.719 --> 00:00:54.000
<v Speaker 1>We really have, so welcome to the deep dives today.

20
00:00:54.479 --> 00:00:57.479
<v Speaker 1>We are starting with this exact premise. Every single time

21
00:00:57.520 --> 00:00:59.920
<v Speaker 1>you click a link or send a request, the serve

22
00:01:00.479 --> 00:01:02.399
<v Speaker 1>basically suffers from total amnesia.

23
00:01:02.479 --> 00:01:05.239
<v Speaker 4>It completely forgets who you are, right, it forgets what

24
00:01:05.280 --> 00:01:08.319
<v Speaker 4>you're doing, and it has to frantically ask a database

25
00:01:08.439 --> 00:01:11.719
<v Speaker 4>to reconstruct your entire identity before can send you a

26
00:01:11.719 --> 00:01:12.680
<v Speaker 4>single web page.

27
00:01:12.840 --> 00:01:15.359
<v Speaker 3>And I mean this approach of going stateless it really

28
00:01:15.400 --> 00:01:18.159
<v Speaker 3>is what allowed the web to scale globally in the

29
00:01:18.159 --> 00:01:18.680
<v Speaker 3>first place.

30
00:01:18.719 --> 00:01:20.599
<v Speaker 1>Sure, because it frees up resources.

31
00:01:20.159 --> 00:01:24.400
<v Speaker 3>Exactly by completely forgetting about you. The second and HTTP

32
00:01:24.599 --> 00:01:27.719
<v Speaker 3>response is sent, servers free up their memory to handle

33
00:01:27.760 --> 00:01:31.480
<v Speaker 3>the next user. But the architectural trade off there is severe.

34
00:01:31.680 --> 00:01:32.280
<v Speaker 1>It really is.

35
00:01:32.519 --> 00:01:35.519
<v Speaker 3>It created massive network latency, and it turned the database

36
00:01:35.599 --> 00:01:42.480
<v Speaker 3>into this unavoidable central bottleneck. Every user action basically requires

37
00:01:42.519 --> 00:01:44.840
<v Speaker 3>an expensive round trip ticket to a hard drive.

38
00:01:44.680 --> 00:01:47.640
<v Speaker 1>Somewhere, which brings us to our mission. For this deep dive.

39
00:01:48.040 --> 00:01:50.519
<v Speaker 1>We are exploring an early beta copy of a book

40
00:01:50.560 --> 00:01:54.120
<v Speaker 1>by Lance Halverson. It's called Functional Web Development with alixer

41
00:01:54.200 --> 00:01:55.040
<v Speaker 1>otp in.

42
00:01:55.000 --> 00:01:56.879
<v Speaker 2>Phoenix, a fantastic read, by the way.

43
00:01:56.959 --> 00:01:59.439
<v Speaker 1>Yeah, and the goal here is pretty radical. We're looking

44
00:01:59.480 --> 00:02:03.879
<v Speaker 1>at how to build blazing fast, stateful applications that remember

45
00:02:03.920 --> 00:02:07.599
<v Speaker 1>everything bypass the database entirely and kind of rethink web

46
00:02:07.680 --> 00:02:08.879
<v Speaker 1>architecture from the ground up.

47
00:02:09.080 --> 00:02:12.360
<v Speaker 3>And to prove this can actually work in production. The

48
00:02:12.400 --> 00:02:16.199
<v Speaker 3>source material walks us through building a complex stateful game

49
00:02:16.240 --> 00:02:21.479
<v Speaker 3>engine entirely in memory, like without touching a single database table.

50
00:02:21.520 --> 00:02:25.000
<v Speaker 1>Okay, let's unpack this. To really understand how this works,

51
00:02:25.400 --> 00:02:27.120
<v Speaker 1>we need to ground it in the game we are

52
00:02:27.159 --> 00:02:30.840
<v Speaker 1>actually building. It's a two player grid based game called Islands.

53
00:02:31.039 --> 00:02:33.639
<v Speaker 2>Yeah, kind of like a classic naval combat.

54
00:02:33.240 --> 00:02:36.919
<v Speaker 1>Game, right exactly. So imagine two players, each with a

55
00:02:36.960 --> 00:02:39.439
<v Speaker 1>ten x ten board cord. It's from a one down

56
00:02:39.439 --> 00:02:42.360
<v Speaker 1>to j ten. Neither player can see the other's.

57
00:02:42.080 --> 00:02:45.319
<v Speaker 3>Board, and you're placing islands of different shapes onto your grid, right,

58
00:02:45.400 --> 00:02:48.000
<v Speaker 3>like an a tall, a single dot, an L shape,

59
00:02:48.080 --> 00:02:51.039
<v Speaker 3>and S shape and a square yep. Once you set them,

60
00:02:51.240 --> 00:02:54.960
<v Speaker 3>you take turns guessing coordinates on your opponent's board. If

61
00:02:55.000 --> 00:02:57.520
<v Speaker 3>you guess right, you plant a palm tree on that island,

62
00:02:57.800 --> 00:03:00.719
<v Speaker 3>get all the coordinates, and you force the island first

63
00:03:00.800 --> 00:03:02.520
<v Speaker 3>to forrest all opponent islands.

64
00:03:02.560 --> 00:03:02.919
<v Speaker 2>Wins.

65
00:03:03.560 --> 00:03:06.159
<v Speaker 1>It is a perfect test case, honestly, because a game

66
00:03:06.280 --> 00:03:08.919
<v Speaker 1>like this is inherently stateful. You have to remember where

67
00:03:08.919 --> 00:03:11.680
<v Speaker 1>the pieces are, whose turn it is, what has already

68
00:03:11.719 --> 00:03:12.159
<v Speaker 1>been guessed.

69
00:03:12.240 --> 00:03:14.240
<v Speaker 2>You can't just forget everything between turns.

70
00:03:14.520 --> 00:03:17.280
<v Speaker 1>Right. If you tried to build this with a traditional

71
00:03:17.319 --> 00:03:21.520
<v Speaker 1>stateless web framework, say standard Rubyon rails or Django app,

72
00:03:22.199 --> 00:03:25.680
<v Speaker 1>every single guess would require a round trip to a

73
00:03:25.719 --> 00:03:28.800
<v Speaker 1>postgress database just to do one move exactly. It would

74
00:03:28.800 --> 00:03:31.599
<v Speaker 1>have to lock the row, check the board, update the score,

75
00:03:31.759 --> 00:03:34.199
<v Speaker 1>save the turn. It'd be incredibly heavy.

76
00:03:34.560 --> 00:03:37.199
<v Speaker 3>Not to mention the concurrency issues if two things happen

77
00:03:37.240 --> 00:03:37.599
<v Speaker 3>at once.

78
00:03:38.240 --> 00:03:40.919
<v Speaker 1>So to build this without a database, the author introduces

79
00:03:41.039 --> 00:03:44.919
<v Speaker 1>us to Alixer and the Erlang virtual machine, which is

80
00:03:44.960 --> 00:03:47.919
<v Speaker 1>known as the Beam. The Beam is just phenomenal, it

81
00:03:48.000 --> 00:03:49.840
<v Speaker 1>really is, and this is where my mind was blown.

82
00:03:50.280 --> 00:03:53.680
<v Speaker 1>The Beam allows you to spawn millions of these ultra lightweight,

83
00:03:53.840 --> 00:03:57.439
<v Speaker 1>long lived processes that hold data right in the server's.

84
00:03:57.120 --> 00:03:58.319
<v Speaker 2>Memory, millions of them.

85
00:03:58.439 --> 00:04:00.439
<v Speaker 1>But I have a major question here. If we have

86
00:04:00.639 --> 00:04:04.560
<v Speaker 1>thousands of different games running simultaneously on one server, spunning

87
00:04:04.639 --> 00:04:08.560
<v Speaker 1>millions of processes, doesn't the memory overhead completely kill the server?

88
00:04:09.120 --> 00:04:11.639
<v Speaker 1>I mean, OS level threads are incredibly heavy.

89
00:04:11.719 --> 00:04:13.000
<v Speaker 2>Well, that's the crucial distinction.

90
00:04:13.080 --> 00:04:15.439
<v Speaker 3>Really, we aren't talking about OS threads here or even

91
00:04:15.599 --> 00:04:18.759
<v Speaker 3>you know, Docker containers. A Beam process is an entirely

92
00:04:18.759 --> 00:04:22.439
<v Speaker 3>different beast. How so, it starts at around two kilobytes

93
00:04:22.480 --> 00:04:25.560
<v Speaker 3>of memory. It is unbelievably lightweight.

94
00:04:25.680 --> 00:04:26.879
<v Speaker 1>Two kilobytes wow.

95
00:04:27.040 --> 00:04:31.000
<v Speaker 3>Yeah, And the Beam has its own internal scheduler that

96
00:04:31.040 --> 00:04:35.680
<v Speaker 3>handles context switching between these processes efficiently. It does this

97
00:04:35.759 --> 00:04:38.720
<v Speaker 3>without ever asking the host operating system for permission.

98
00:04:38.800 --> 00:04:39.319
<v Speaker 1>That's wild.

99
00:04:39.439 --> 00:04:42.639
<v Speaker 3>It allocates a certain number of reductions or function calls

100
00:04:42.759 --> 00:04:46.000
<v Speaker 3>to each process before forcing it to yield. That's actually

101
00:04:46.040 --> 00:04:49.879
<v Speaker 3>how it handles millions of concurrent tasks without blocking the

102
00:04:49.920 --> 00:04:50.439
<v Speaker 3>main thread.

103
00:04:50.639 --> 00:04:54.000
<v Speaker 1>Okay, so they are cheap to make, But how do

104
00:04:54.040 --> 00:04:55.839
<v Speaker 1>we prevent the data from getting scrambled?

105
00:04:56.120 --> 00:04:56.560
<v Speaker 2>What do you mean?

106
00:04:56.759 --> 00:04:58.800
<v Speaker 1>Well, in a normal web app, if two people click

107
00:04:58.839 --> 00:05:01.680
<v Speaker 1>a button at the exact same millisecond, you get race conditions.

108
00:05:02.079 --> 00:05:04.560
<v Speaker 1>Data gets overwritten unless you have a heady database.

109
00:05:04.279 --> 00:05:05.199
<v Speaker 2>Lock right right?

110
00:05:05.759 --> 00:05:08.120
<v Speaker 1>Is this setup sort of like having a goldfish teller

111
00:05:08.160 --> 00:05:10.160
<v Speaker 1>at a bank who has to run back and check

112
00:05:10.240 --> 00:05:13.839
<v Speaker 1>the main vault for every single transaction, versus having a

113
00:05:13.839 --> 00:05:17.120
<v Speaker 1>dedicated teller who just keeps your specific file open right

114
00:05:17.160 --> 00:05:18.199
<v Speaker 1>on their desk the whole time.

115
00:05:18.639 --> 00:05:21.639
<v Speaker 3>Oh I love that analogy. Yes, it's exactly like the

116
00:05:21.680 --> 00:05:26.000
<v Speaker 3>dedicated teller in Alixer. These dedicated tellers are called agents.

117
00:05:26.360 --> 00:05:30.759
<v Speaker 3>Agents are simple, lightweight processes designed specifically to hold state,

118
00:05:31.079 --> 00:05:31.600
<v Speaker 3>so they.

119
00:05:31.519 --> 00:05:34.199
<v Speaker 1>Literally just sit there holding your data in memory exactly.

120
00:05:34.279 --> 00:05:35.439
<v Speaker 2>And what's fascinating.

121
00:05:35.480 --> 00:05:38.519
<v Speaker 3>Here is how they solve that race condition problem without

122
00:05:38.600 --> 00:05:42.680
<v Speaker 3>database locks. They rely on a principle called single representation.

123
00:05:43.000 --> 00:05:44.680
<v Speaker 1>Okay, what does that mean in practice?

124
00:05:44.879 --> 00:05:47.839
<v Speaker 3>Well, a piece of data, let's say the status of

125
00:05:47.839 --> 00:05:50.920
<v Speaker 3>Coordinate A one on Player one's board. It lives inside

126
00:05:50.920 --> 00:05:54.560
<v Speaker 3>exactly one agent process, nowhere else in the entire system,

127
00:05:54.920 --> 00:05:59.000
<v Speaker 3>and every single beam process has an exclusive built in mailbox.

128
00:05:59.279 --> 00:06:02.079
<v Speaker 1>So wait, if fifty network requests arrive at the exact

129
00:06:02.120 --> 00:06:06.000
<v Speaker 1>same millisecond asking to modify Coordinate A one, what.

130
00:06:05.879 --> 00:06:09.639
<v Speaker 3>Happens in mailbox physically cues them up. The agent processes

131
00:06:09.639 --> 00:06:13.120
<v Speaker 3>those messages sequentially, one by one. It processes message one,

132
00:06:13.279 --> 00:06:15.800
<v Speaker 3>updates its internal state, then pulls message too.

133
00:06:15.879 --> 00:06:17.199
<v Speaker 1>So there's no collision at all.

134
00:06:17.439 --> 00:06:18.279
<v Speaker 2>None.

135
00:06:18.360 --> 00:06:21.879
<v Speaker 3>Because the data has a single representation and a sequential mailbox,

136
00:06:22.199 --> 00:06:27.000
<v Speaker 3>it naturally effortlessly prevents race conditions. The architecture itself makes

137
00:06:27.040 --> 00:06:29.079
<v Speaker 3>a race condition mathematically impossible.

138
00:06:29.240 --> 00:06:32.759
<v Speaker 1>That is incredibly elegant. And because we aren't using a database,

139
00:06:32.800 --> 00:06:35.720
<v Speaker 1>we don't have to deal with object relational mapping or ORM.

140
00:06:35.879 --> 00:06:37.680
<v Speaker 2>Right, you get to skip all of that overhead.

141
00:06:37.759 --> 00:06:40.959
<v Speaker 1>There are no foreign keys, no joint tables, no M

142
00:06:41.000 --> 00:06:45.079
<v Speaker 1>plus one cordy problems. The architecture composes the game entities

143
00:06:45.399 --> 00:06:48.240
<v Speaker 1>using pure native Elixir data structures.

144
00:06:47.759 --> 00:06:51.319
<v Speaker 2>Which eliminates an enormous layer of translation logic. It's so

145
00:06:51.399 --> 00:06:52.040
<v Speaker 2>much cleaner.

146
00:06:52.319 --> 00:06:55.160
<v Speaker 1>So an individual coordinate is an Elixir struct It just

147
00:06:55.199 --> 00:06:57.720
<v Speaker 1>tracks a couple of specific things. A key for in

148
00:06:57.879 --> 00:07:00.439
<v Speaker 1>island to say what island it belongs to, and a

149
00:07:00.439 --> 00:07:02.360
<v Speaker 1>booleion for guests to say if it's been hit.

150
00:07:02.680 --> 00:07:03.199
<v Speaker 2>Very simple.

151
00:07:03.360 --> 00:07:06.680
<v Speaker 1>Yeah, then an island is just a list of those

152
00:07:06.720 --> 00:07:09.639
<v Speaker 1>coordinate processes, and a board is a map that connects

153
00:07:09.639 --> 00:07:12.759
<v Speaker 1>string names like A one to the specific coordinate process.

154
00:07:12.879 --> 00:07:13.000
<v Speaker 3>Ye.

155
00:07:13.160 --> 00:07:15.800
<v Speaker 1>You are literally just nesting native data structures.

156
00:07:16.000 --> 00:07:18.000
<v Speaker 3>But here is the critical part, and this is where

157
00:07:18.040 --> 00:07:21.639
<v Speaker 3>it gets really smart. Because each of these entities, the coordinates,

158
00:07:21.680 --> 00:07:25.439
<v Speaker 3>the islands, the boards are individual agent processes.

159
00:07:25.959 --> 00:07:28.759
<v Speaker 2>They are highly decoupled. Okay, if you update the A.

160
00:07:28.800 --> 00:07:31.879
<v Speaker 3>One coordinate through the board process, the island process that

161
00:07:31.920 --> 00:07:34.560
<v Speaker 3>also contains A one instantly sees the update.

162
00:07:34.800 --> 00:07:36.759
<v Speaker 2>Why is that because the.

163
00:07:36.639 --> 00:07:40.319
<v Speaker 3>Island doesn't hold a deeply nested copy of the coordinate data,

164
00:07:40.480 --> 00:07:43.519
<v Speaker 3>It just holds the address the process ID pointing to

165
00:07:43.560 --> 00:07:45.680
<v Speaker 3>that one specific coordinate agent.

166
00:07:45.839 --> 00:07:48.399
<v Speaker 1>Okay, so we've solved the data layer we have perfect

167
00:07:48.399 --> 00:07:52.600
<v Speaker 1>isolation and no race conditions. But having tens of thousands

168
00:07:52.680 --> 00:07:57.240
<v Speaker 1>of isolated islands of data is useless without a logistics.

169
00:07:56.839 --> 00:07:58.279
<v Speaker 2>Network, right, They need to communicate.

170
00:07:58.480 --> 00:08:00.839
<v Speaker 1>Exactly How do they all coordinate if you have independent

171
00:08:00.839 --> 00:08:03.720
<v Speaker 1>agents holding data, How does that not instantly turn into

172
00:08:03.800 --> 00:08:07.720
<v Speaker 1>a tangled, chaotic mess of processes just shouting at each other.

173
00:08:07.879 --> 00:08:11.040
<v Speaker 3>Well, that dilemma leads us directly to the next architectural layer.

174
00:08:11.480 --> 00:08:14.120
<v Speaker 3>Having thousands of independent agents. It's like having thousands of

175
00:08:14.160 --> 00:08:17.279
<v Speaker 3>employees who refuse to talk to anyone but their direct supervisor.

176
00:08:17.439 --> 00:08:19.040
<v Speaker 1>That sounds like a nightmare. It is.

177
00:08:19.199 --> 00:08:21.920
<v Speaker 3>You need a management structure. In the traditional tech world,

178
00:08:22.000 --> 00:08:24.519
<v Speaker 3>when developers want to break up a massive application to

179
00:08:24.560 --> 00:08:29.480
<v Speaker 3>scale it, they usually turn to service oriented architecture or microservices.

180
00:08:29.480 --> 00:08:32.519
<v Speaker 1>Oh boy, micro services. I feel like every time an

181
00:08:32.559 --> 00:08:36.279
<v Speaker 1>engineering team pivots to microservices, it starts with a hyped

182
00:08:36.360 --> 00:08:39.679
<v Speaker 1>up whiteboard session and ends in absolute tears.

183
00:08:39.799 --> 00:08:41.919
<v Speaker 2>The pain points are notorious. For sure.

184
00:08:42.360 --> 00:08:44.960
<v Speaker 3>To get traditional microservices to talk to each other, you

185
00:08:45.000 --> 00:08:48.279
<v Speaker 3>have to deal with incredibly complex network topologies.

186
00:08:48.559 --> 00:08:52.480
<v Speaker 2>Oh the retrilogic alone, exactly. You have to write massive

187
00:08:52.519 --> 00:08:56.519
<v Speaker 2>amounts of retrilogic just to handle network failures. Because the

188
00:08:56.559 --> 00:09:01.000
<v Speaker 2>services are communicating over the open Internet via HTTP, testing

189
00:09:01.000 --> 00:09:03.159
<v Speaker 2>them end to end is a nightmare.

190
00:09:02.840 --> 00:09:04.679
<v Speaker 1>And local development is just as bad.

191
00:09:04.840 --> 00:09:06.960
<v Speaker 3>Just to spin up a dev environment on your laptop,

192
00:09:07.039 --> 00:09:10.279
<v Speaker 3>you might have to run Docker, Kubernetes, a dozen different ports,

193
00:09:10.320 --> 00:09:13.159
<v Speaker 3>and configure external message cues like Kafka.

194
00:09:13.279 --> 00:09:14.200
<v Speaker 2>It's brutal.

195
00:09:14.320 --> 00:09:17.360
<v Speaker 1>But the architecture we're looking at offers a completely different path,

196
00:09:17.759 --> 00:09:21.559
<v Speaker 1>using OTP, the Open Telecom platform, and a specific abstraction

197
00:09:22.000 --> 00:09:24.720
<v Speaker 1>called a GEN server, which stands for Generic server.

198
00:09:25.000 --> 00:09:28.480
<v Speaker 3>Right. A GEN server gives us the exact encapsulation and

199
00:09:28.559 --> 00:09:32.000
<v Speaker 3>separation of concerns of a micro service, but it runs

200
00:09:32.200 --> 00:09:34.200
<v Speaker 3>entirely inside our Elixir application.

201
00:09:34.399 --> 00:09:35.480
<v Speaker 1>That is a huge difference.

202
00:09:35.519 --> 00:09:38.480
<v Speaker 3>It lives inside the Beam virtual machine, so you get

203
00:09:38.519 --> 00:09:42.000
<v Speaker 3>the benefits of a service oriented architecture, but with zero

204
00:09:42.200 --> 00:09:47.879
<v Speaker 3>external infrastructure, no HTTP network ops, no JSON serialization, no

205
00:09:48.080 --> 00:09:49.600
<v Speaker 3>Kubernetes cluster required.

206
00:09:50.039 --> 00:09:53.360
<v Speaker 1>Here's where it gets really interesting. The book insists on

207
00:09:53.399 --> 00:09:57.039
<v Speaker 1>a very specific repeatable code pattern for building these gen servers.

208
00:09:57.639 --> 00:10:00.559
<v Speaker 1>He mandates that a client function wraps a mod function,

209
00:10:00.840 --> 00:10:02.279
<v Speaker 1>which then triggers a callback.

210
00:10:02.440 --> 00:10:04.159
<v Speaker 2>Yep, the standard OTP pattern.

211
00:10:04.240 --> 00:10:06.200
<v Speaker 1>But why go through all these layers why not just

212
00:10:06.279 --> 00:10:08.320
<v Speaker 1>have a function that reaches in and grabs.

213
00:10:08.000 --> 00:10:10.080
<v Speaker 3>The data, because we have to remember how a genser

214
00:10:10.320 --> 00:10:14.279
<v Speaker 3>actually holds state underneath the hood, a gen service basically

215
00:10:14.320 --> 00:10:16.759
<v Speaker 3>just a recursive function running in an infinite loop.

216
00:10:16.960 --> 00:10:17.279
<v Speaker 1>Okay.

217
00:10:17.559 --> 00:10:19.840
<v Speaker 3>It receives a message, computes a new state, and then

218
00:10:19.879 --> 00:10:22.799
<v Speaker 3>calls itself recursively with that new state. If you let

219
00:10:22.919 --> 00:10:25.960
<v Speaker 3>other parts of your app interact directly with that recursive loop,

220
00:10:26.080 --> 00:10:27.600
<v Speaker 3>you break the concurrency model.

221
00:10:27.879 --> 00:10:31.000
<v Speaker 1>Ah see. So the client function is the public API.

222
00:10:31.120 --> 00:10:33.000
<v Speaker 1>It's the safe interface that the rest of the app

223
00:10:33.080 --> 00:10:34.080
<v Speaker 1>uses exactly.

224
00:10:34.600 --> 00:10:38.039
<v Speaker 3>The client function sends a message to the gen server process.

225
00:10:38.600 --> 00:10:42.679
<v Speaker 3>The gen server process receives it, triggers the internal callback function,

226
00:10:43.240 --> 00:10:47.720
<v Speaker 3>processes the state change, and continues its loop safely. It

227
00:10:47.759 --> 00:10:51.200
<v Speaker 3>completely isolates the internal state from the rest of the system.

228
00:10:51.360 --> 00:10:53.639
<v Speaker 1>And as part of this messaging, we dive into the

229
00:10:53.639 --> 00:10:55.799
<v Speaker 1>difference between a call and a cast.

230
00:10:56.159 --> 00:10:57.639
<v Speaker 2>This is a really important distinction.

231
00:10:57.759 --> 00:10:59.559
<v Speaker 1>Yeah. So, if I'm a process sending a message to

232
00:10:59.559 --> 00:11:02.480
<v Speaker 1>a gens wherever, a call is synchronous, I knock on

233
00:11:02.519 --> 00:11:05.000
<v Speaker 1>the door, ask a question, and I wait for the answer.

234
00:11:05.440 --> 00:11:07.840
<v Speaker 1>A cast is a synchronous I throw the message over

235
00:11:07.879 --> 00:11:10.679
<v Speaker 1>the wall and walk away, right, But the author strongly

236
00:11:10.720 --> 00:11:13.799
<v Speaker 1>advocates for using call almost everywhere, even when you don't

237
00:11:13.799 --> 00:11:17.840
<v Speaker 1>actually need the return data. Why why deliberately slow the

238
00:11:17.879 --> 00:11:19.399
<v Speaker 1>system down by making it.

239
00:11:19.399 --> 00:11:24.279
<v Speaker 3>Weight Because call creates a mechanical, unhackable system of back pressure.

240
00:11:24.720 --> 00:11:28.600
<v Speaker 3>When you execute a call, the beam scheduler literally suspends

241
00:11:28.639 --> 00:11:31.200
<v Speaker 3>the execution thread of the sender until the gen.

242
00:11:31.120 --> 00:11:33.200
<v Speaker 1>Server replies, So it's a built in traffic light.

243
00:11:33.480 --> 00:11:37.200
<v Speaker 3>Precisely, if a gen server is getting overwhelmed with messages,

244
00:11:37.279 --> 00:11:41.759
<v Speaker 3>say a massive spike in user traffic, a call physically

245
00:11:41.840 --> 00:11:44.320
<v Speaker 3>forces the senders to pause and wait their turn.

246
00:11:44.759 --> 00:11:46.759
<v Speaker 2>It prevents the system from flooding.

247
00:11:46.519 --> 00:11:48.720
<v Speaker 1>Because with a cast it would just keep piling up.

248
00:11:48.799 --> 00:11:52.639
<v Speaker 3>Exactly with an asynchronous cast, you could bombard a process

249
00:11:52.679 --> 00:11:55.840
<v Speaker 3>with a million messages in a second, completely fill up

250
00:11:55.879 --> 00:11:59.799
<v Speaker 3>its mailbox memory, and crash the process. Using a call

251
00:12:00.080 --> 00:12:02.960
<v Speaker 3>even if the reply is just an empty okay, keeps

252
00:12:03.000 --> 00:12:05.519
<v Speaker 3>the entire memory footprint stable and predictable.

253
00:12:05.799 --> 00:12:08.279
<v Speaker 1>Okay. That makes perfect sense for stability, and it's that

254
00:12:08.360 --> 00:12:11.039
<v Speaker 1>stability that allows us to build out the game logic

255
00:12:11.399 --> 00:12:12.679
<v Speaker 1>using the law of demeter.

256
00:12:12.639 --> 00:12:13.879
<v Speaker 2>A great design principle.

257
00:12:13.960 --> 00:12:16.879
<v Speaker 1>Yeah, it's where a process only talks to its immediate friends.

258
00:12:17.200 --> 00:12:19.840
<v Speaker 1>The main game gen server doesn't reach deep down and

259
00:12:19.919 --> 00:12:21.039
<v Speaker 1>directly tweak a.

260
00:12:21.039 --> 00:12:24.759
<v Speaker 3>Coordinate because that would create brittle, highly coupled code. If

261
00:12:24.799 --> 00:12:27.960
<v Speaker 3>the game process knows exactly how a coordinate works, any

262
00:12:28.080 --> 00:12:29.799
<v Speaker 3>change to the coordinate breaks.

263
00:12:29.519 --> 00:12:30.240
<v Speaker 2>The game right.

264
00:12:30.279 --> 00:12:33.600
<v Speaker 1>So instead the architecture change the functions. The game process

265
00:12:33.639 --> 00:12:36.279
<v Speaker 1>talks to the player process, the player process talks to

266
00:12:36.320 --> 00:12:39.000
<v Speaker 1>the board map, and the board talks to the specific

267
00:12:39.039 --> 00:12:39.799
<v Speaker 1>coordinate agent.

268
00:12:40.000 --> 00:12:42.320
<v Speaker 2>It delegates the work down the chain exactly.

269
00:12:42.759 --> 00:12:44.960
<v Speaker 1>If you want to change how a coordinate works, you

270
00:12:45.120 --> 00:12:46.519
<v Speaker 1>only update the coordinate code.

271
00:12:46.519 --> 00:12:47.879
<v Speaker 2>It's beautifully isolated.

272
00:12:48.240 --> 00:12:50.200
<v Speaker 1>But wait, I have to push back here for a second.

273
00:12:50.720 --> 00:12:53.600
<v Speaker 1>If we have ten thousand active games being played on

274
00:12:53.600 --> 00:12:57.000
<v Speaker 1>our server, we have ten thousand game gen servers running.

275
00:12:57.840 --> 00:13:02.320
<v Speaker 1>How does a player's web request actually their specific game process?

276
00:13:02.639 --> 00:13:04.200
<v Speaker 2>Ah, the routing problem.

277
00:13:04.320 --> 00:13:06.720
<v Speaker 1>Yeah, I know. Erlaying has a built in way to

278
00:13:06.840 --> 00:13:11.120
<v Speaker 1>name processes using atoms, which are basically constant strings. But

279
00:13:11.159 --> 00:13:14.159
<v Speaker 1>there's a massive warning in the source material about using

280
00:13:14.240 --> 00:13:18.240
<v Speaker 1>atoms for dynamically generated games. Why can't we just name

281
00:13:18.279 --> 00:13:19.799
<v Speaker 1>the process with an atom?

282
00:13:20.000 --> 00:13:22.039
<v Speaker 3>Well, it's a fatal trap if you don't understand how

283
00:13:22.039 --> 00:13:25.799
<v Speaker 3>the beam handles memory atoms are incredibly fast because under

284
00:13:25.840 --> 00:13:28.240
<v Speaker 3>the hood, an atom is just an integer pointer to

285
00:13:28.279 --> 00:13:30.320
<v Speaker 3>an entry in a C level string table.

286
00:13:30.360 --> 00:13:31.480
<v Speaker 1>Okay, fast sounds good.

287
00:13:31.720 --> 00:13:34.159
<v Speaker 3>It is, But because they are just pointers, atoms are

288
00:13:34.200 --> 00:13:36.759
<v Speaker 3>never garbage collected. Once you create an atom, it stays

289
00:13:36.759 --> 00:13:39.919
<v Speaker 3>in that table forever. And the Erling virtual machine has

290
00:13:39.960 --> 00:13:43.480
<v Speaker 3>a hard unchangeable limit of just over one million atoms.

291
00:13:43.879 --> 00:13:46.919
<v Speaker 1>Oh wow. So if you dynamically name every new game

292
00:13:47.240 --> 00:13:49.639
<v Speaker 1>with an atom based on a user's ID.

293
00:13:49.679 --> 00:13:52.759
<v Speaker 3>You will eventually hit that one million limit. And when

294
00:13:52.759 --> 00:13:55.320
<v Speaker 3>the atom table fills up, the node doesn't just throw

295
00:13:55.320 --> 00:13:57.159
<v Speaker 3>an exception, it violently crashes.

296
00:13:57.639 --> 00:13:59.759
<v Speaker 2>The entire server process drops dead.

297
00:14:00.120 --> 00:14:03.240
<v Speaker 3>Oh man, Yeah, it's a classic memory leak that causes

298
00:14:03.240 --> 00:14:05.799
<v Speaker 3>three point zero zero AM emergency outages.

299
00:14:05.960 --> 00:14:08.200
<v Speaker 1>Okay, but if atoms are a hard limit and they

300
00:14:08.200 --> 00:14:11.639
<v Speaker 1>crash the node, why use them at all? Why not

301
00:14:11.799 --> 00:14:14.200
<v Speaker 1>just use normal strings for everything? From the beginning? And

302
00:14:14.240 --> 00:14:16.639
<v Speaker 1>what's the actual solution for routing to our game?

303
00:14:16.960 --> 00:14:20.159
<v Speaker 3>We use a global name registry. Instead of registering the

304
00:14:20.200 --> 00:14:22.919
<v Speaker 3>gen server with a primitive atom, we register it using

305
00:14:22.960 --> 00:14:25.879
<v Speaker 3>a standard string like game call in player name.

306
00:14:25.960 --> 00:14:27.720
<v Speaker 1>Because strings are garbage collected.

307
00:14:27.519 --> 00:14:30.639
<v Speaker 2>Exactly, strings are garbage collected. The global registry keeps a

308
00:14:30.720 --> 00:14:33.879
<v Speaker 2>dynamic track of which string points to which internal process

309
00:14:33.919 --> 00:14:35.960
<v Speaker 2>idea across the entire server cluster.

310
00:14:36.200 --> 00:14:39.879
<v Speaker 1>But isn't a global string registry a central bottleneck If

311
00:14:40.000 --> 00:14:43.120
<v Speaker 1>every single request has to query one central registry to

312
00:14:43.159 --> 00:14:47.840
<v Speaker 1>find its destination, aren't we just reinventing the database bottleneck

313
00:14:47.840 --> 00:14:48.559
<v Speaker 1>and memory.

314
00:14:48.600 --> 00:14:51.879
<v Speaker 3>It would be, except for how Elixir implements the registry.

315
00:14:52.720 --> 00:14:57.320
<v Speaker 3>It uses ETS or laining term storage. It's an extremely

316
00:14:57.559 --> 00:15:01.799
<v Speaker 3>fast concurrent in memory to built in c Okay, so

317
00:15:01.840 --> 00:15:05.440
<v Speaker 3>it's optimized, very optimized. Multiple processes can read from an

318
00:15:05.480 --> 00:15:09.159
<v Speaker 3>ETS table simultaneously without blocking each other, so the lookups

319
00:15:09.200 --> 00:15:13.799
<v Speaker 3>are effectively instantaneous and highly concurrent. It completely bypasses the

320
00:15:13.840 --> 00:15:16.159
<v Speaker 3>atom limit crash without introducing a bottleneck.

321
00:15:16.240 --> 00:15:18.159
<v Speaker 1>Okay, so let's zoom out a bit. We have our

322
00:15:18.279 --> 00:15:21.600
<v Speaker 1>data living cleanly in memory using agents. We have perfect

323
00:15:21.720 --> 00:15:25.600
<v Speaker 1>robust communication and coordination using gen servers. But knowing where

324
00:15:25.639 --> 00:15:28.120
<v Speaker 1>the pieces are is really only half the battle.

325
00:15:28.200 --> 00:15:30.639
<v Speaker 2>Because a game has phases exactly, it has.

326
00:15:30.559 --> 00:15:33.159
<v Speaker 1>The dimension of time. If player one tries to drop

327
00:15:33.200 --> 00:15:35.480
<v Speaker 1>a bomb before player two is even joined the server,

328
00:15:35.919 --> 00:15:38.720
<v Speaker 1>our perfect data layer is going to flawlessly record an

329
00:15:38.720 --> 00:15:41.799
<v Speaker 1>illegal move. How do we build a bouncer that enforces

330
00:15:41.799 --> 00:15:44.639
<v Speaker 1>the rules of time and sequence without writing a billion

331
00:15:44.679 --> 00:15:45.960
<v Speaker 1>MESSIFL statements.

332
00:15:46.120 --> 00:15:48.600
<v Speaker 3>This raise is an important question, and to see why

333
00:15:48.639 --> 00:15:51.399
<v Speaker 3>this is so difficult, just look at the nightmare of

334
00:15:51.480 --> 00:15:52.279
<v Speaker 3>modern front.

335
00:15:52.120 --> 00:15:52.879
<v Speaker 2>End state management.

336
00:15:53.159 --> 00:15:54.600
<v Speaker 1>To start on front end state.

337
00:15:54.879 --> 00:15:58.960
<v Speaker 3>Right, You've got react use effect hooks, misfiring reduc stores,

338
00:15:59.000 --> 00:16:03.279
<v Speaker 3>getting tangled components, rendering out of order. State fatigue is

339
00:16:03.519 --> 00:16:06.960
<v Speaker 3>very real. When an application stays alive in memory, you

340
00:16:07.000 --> 00:16:10.240
<v Speaker 3>have to manually track every single user interaction over time.

341
00:16:10.360 --> 00:16:13.720
<v Speaker 1>Yeah, if modal A is open, disable button B, but

342
00:16:13.799 --> 00:16:15.559
<v Speaker 1>only if user C is an admin.

343
00:16:15.759 --> 00:16:19.480
<v Speaker 2>It devolves into a massive, tangled web of conditional logic.

344
00:16:19.720 --> 00:16:23.159
<v Speaker 1>So how does this Elixir architecture prevent that exact same

345
00:16:23.200 --> 00:16:24.639
<v Speaker 1>spaghetti logic on the back end.

346
00:16:24.720 --> 00:16:27.720
<v Speaker 3>It avoids it by using another built in OTP behavior

347
00:16:27.799 --> 00:16:31.159
<v Speaker 3>called gen statum, which stands for a generic state machine.

348
00:16:31.720 --> 00:16:35.639
<v Speaker 3>A state machine acts as the ultimate uncompromising rule enforcer.

349
00:16:36.159 --> 00:16:39.639
<v Speaker 3>It separates the state management logic entirely from the business logic.

350
00:16:39.840 --> 00:16:42.440
<v Speaker 1>Let's walk through exactly how this is implemented for our game.

351
00:16:42.879 --> 00:16:45.559
<v Speaker 1>The game starts in a state called initialized, and the

352
00:16:45.600 --> 00:16:48.879
<v Speaker 1>brilliance of gen statum is how literal the code pattern is.

353
00:16:49.240 --> 00:16:52.039
<v Speaker 1>You don't write a giant switch statement checking a state variable.

354
00:16:52.080 --> 00:16:52.600
<v Speaker 2>No, you don't.

355
00:16:52.720 --> 00:16:56.200
<v Speaker 1>You actually write a callback function and physically name it initialized.

356
00:16:56.279 --> 00:16:57.639
<v Speaker 2>The function name is the state.

357
00:16:58.080 --> 00:17:02.399
<v Speaker 1>So inside that initialized function, the code explicitly says the

358
00:17:02.440 --> 00:17:06.240
<v Speaker 1>only permissible event here is adding a second player. If

359
00:17:06.279 --> 00:17:09.839
<v Speaker 1>player two joins, the function returns a success topple and

360
00:17:09.880 --> 00:17:12.920
<v Speaker 1>then instructs the state machine to transition the entire game

361
00:17:13.279 --> 00:17:16.039
<v Speaker 1>into a completely new state called player's set.

362
00:17:16.160 --> 00:17:17.119
<v Speaker 2>And this is the magic.

363
00:17:17.319 --> 00:17:19.519
<v Speaker 3>Once you are in the players set state, the beam

364
00:17:19.599 --> 00:17:24.240
<v Speaker 3>process actually changes which function handles the message loop. Really, yeah,

365
00:17:24.519 --> 00:17:28.240
<v Speaker 3>the initialized function is no longer active. Any new messages

366
00:17:28.319 --> 00:17:31.480
<v Speaker 3>are now routed exclusively to a function called player's set.

367
00:17:31.599 --> 00:17:33.880
<v Speaker 1>But what if a rogue event tries to sneak in.

368
00:17:34.200 --> 00:17:36.119
<v Speaker 1>What if there's a bug in the front end client

369
00:17:36.279 --> 00:17:38.559
<v Speaker 1>and it sends a command trying to guess a coordinate

370
00:17:38.839 --> 00:17:41.839
<v Speaker 1>while we are still in the initialized phase. If the

371
00:17:41.880 --> 00:17:45.319
<v Speaker 1>initialized function only knows how to handle adding a second player,

372
00:17:46.079 --> 00:17:48.759
<v Speaker 1>won't that unhandled message crash the process?

373
00:17:49.119 --> 00:17:50.920
<v Speaker 3>That is where the catch all clause comes in, and

374
00:17:50.960 --> 00:17:54.240
<v Speaker 3>it showcases the power of Elixer's pattern matching it's less

375
00:17:54.240 --> 00:17:57.279
<v Speaker 3>like a standard else's statement and more like a coin sorting.

376
00:17:56.960 --> 00:17:58.559
<v Speaker 1>Machine the coinsorder. Yeah.

377
00:17:58.640 --> 00:18:00.640
<v Speaker 3>When an event drops in at the top, Elixra tries

378
00:18:00.640 --> 00:18:02.799
<v Speaker 3>to match its shape against the defined rules.

379
00:18:03.039 --> 00:18:05.359
<v Speaker 1>So if it's the exact right size for the ad

380
00:18:05.359 --> 00:18:08.880
<v Speaker 1>player slot, it falls through and updates the state exactly.

381
00:18:09.440 --> 00:18:12.359
<v Speaker 3>But if it's a misshapen rogue event, like guessing a

382
00:18:12.440 --> 00:18:16.559
<v Speaker 3>coordinate too early, it slides past that slot. At the

383
00:18:16.680 --> 00:18:19.559
<v Speaker 3>very bottom of your state function, you define a catch.

384
00:18:19.319 --> 00:18:21.119
<v Speaker 1>All clause, and what does that do?

385
00:18:21.160 --> 00:18:23.319
<v Speaker 3>The rogue event slides all the way down to this

386
00:18:23.400 --> 00:18:26.279
<v Speaker 3>bottom slot, where it's safely rejected into the coin return.

387
00:18:27.079 --> 00:18:30.240
<v Speaker 3>It physically cannot get stuck in the gears. The catch

388
00:18:30.240 --> 00:18:34.319
<v Speaker 3>all simply returns an aerotople, denies the transition, and keeps

389
00:18:34.319 --> 00:18:36.160
<v Speaker 3>the game safely in its current state.

390
00:18:36.559 --> 00:18:40.880
<v Speaker 1>It completely eliminates the need for sprawling nested if statements.

391
00:18:41.680 --> 00:18:44.599
<v Speaker 1>The rules of time and sequence are strictly enforced by

392
00:18:44.640 --> 00:18:47.240
<v Speaker 1>the physical routing of the architecture itself.

393
00:18:46.839 --> 00:18:49.160
<v Speaker 3>And the beauty is that the business logic doesn't have

394
00:18:49.200 --> 00:18:51.920
<v Speaker 3>to care about time anymore. The logic for calculating a

395
00:18:52.000 --> 00:18:54.759
<v Speaker 3>hit or a miss only exists inside the state where

396
00:18:54.759 --> 00:18:56.440
<v Speaker 3>guessing is mathematically allowed.

397
00:18:56.680 --> 00:18:58.759
<v Speaker 1>So what does this all mean? We started by talking

398
00:18:58.799 --> 00:19:01.680
<v Speaker 1>about the amnesia of the mod web, the heavy reliance

399
00:19:01.720 --> 00:19:04.400
<v Speaker 1>on databases just to remember who a user is, and

400
00:19:04.519 --> 00:19:06.200
<v Speaker 1>the immense latency that causes.

401
00:19:06.279 --> 00:19:07.799
<v Speaker 2>We covered a lot of ground today.

402
00:19:07.640 --> 00:19:11.079
<v Speaker 1>We did, but by dissecting this architecture we've seen a

403
00:19:11.079 --> 00:19:14.960
<v Speaker 1>completely different paradigm. We've seen how moving away from the

404
00:19:15.000 --> 00:19:19.720
<v Speaker 1>database as a crutch and embracing stateful, concurrent processes can

405
00:19:19.759 --> 00:19:22.240
<v Speaker 1>result in incredibly fast, robust applications.

406
00:19:22.519 --> 00:19:24.839
<v Speaker 3>It proves that you can have the elegance of tiny,

407
00:19:25.039 --> 00:19:29.160
<v Speaker 3>focused single purpose functions and compose them together to run

408
00:19:29.240 --> 00:19:31.759
<v Speaker 3>massive complex systems entirely natively.

409
00:19:32.519 --> 00:19:33.839
<v Speaker 2>No RMS, no.

410
00:19:34.119 --> 00:19:39.160
<v Speaker 3>HTTP, microservice tangles across external networks, no JavaScript state fatigue,

411
00:19:39.880 --> 00:19:44.200
<v Speaker 3>just clean, highly concurrent in memory processing managed by the beam.

412
00:19:44.519 --> 00:19:46.960
<v Speaker 1>And that leads us to a final provocative thought for

413
00:19:47.000 --> 00:19:49.160
<v Speaker 1>you to take away from this deep dive. We just

414
00:19:49.240 --> 00:19:51.880
<v Speaker 1>walked through how to run a complex real time multiplayer

415
00:19:51.880 --> 00:19:55.079
<v Speaker 1>game entirely in memory, using lightweight processes and state machines

416
00:19:55.359 --> 00:19:57.200
<v Speaker 1>without ever touching a database.

417
00:19:56.839 --> 00:19:58.240
<v Speaker 2>Which is the feat in itself.

418
00:19:58.359 --> 00:20:00.119
<v Speaker 1>It is, but games are really just the ti up

419
00:20:00.119 --> 00:20:03.720
<v Speaker 1>of the iceberg. What other standard notoriously database heavy business

420
00:20:03.720 --> 00:20:07.079
<v Speaker 1>applications can be completely revolutionized by this architecture.

421
00:20:07.240 --> 00:20:10.240
<v Speaker 3>It's a profound call to mullover. Just think about complex

422
00:20:10.279 --> 00:20:13.599
<v Speaker 3>e commerce shopping carts that constantly query the database just

423
00:20:13.640 --> 00:20:15.160
<v Speaker 3>hold five items.

424
00:20:15.160 --> 00:20:18.519
<v Speaker 1>Or highly secure banking sessions where state must be.

425
00:20:18.400 --> 00:20:23.240
<v Speaker 3>Perfectly preserved exactly, or live logistics networks tracking thousands of

426
00:20:23.240 --> 00:20:26.079
<v Speaker 3>delivery trucks in real time. If we can simply allow

427
00:20:26.079 --> 00:20:28.319
<v Speaker 3>the server to remember who we are and hold that

428
00:20:28.400 --> 00:20:31.960
<v Speaker 3>state securely and concurrently in memory, the latency drops to

429
00:20:32.000 --> 00:20:32.640
<v Speaker 3>almost zero.

430
00:20:32.839 --> 00:20:33.880
<v Speaker 1>It's amazing to think about.

431
00:20:34.079 --> 00:20:37.000
<v Speaker 2>The possibilities for scale and speed are enormous.

432
00:20:37.160 --> 00:20:40.440
<v Speaker 1>Imagine your barista not only remembering your name and your

433
00:20:40.519 --> 00:20:44.319
<v Speaker 1>exact order, but having it poured and ready the exact

434
00:20:44.359 --> 00:20:47.160
<v Speaker 1>second you walk through the door without ever having to

435
00:20:47.160 --> 00:20:49.960
<v Speaker 1>look at a ledger. That is the true power of

436
00:20:49.960 --> 00:20:52.880
<v Speaker 1>a staatefle architecture. We want to warmly thank you for

437
00:20:52.960 --> 00:20:55.680
<v Speaker 1>joining us on this deep dive today. Keep questioning the

438
00:20:55.680 --> 00:20:58.200
<v Speaker 1>standard ways of doing things, and we'll see you next time.
