-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathusing-context.slide
295 lines (180 loc) · 7.72 KB
/
using-context.slide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
Understanding Go context package
24 December 2016
Kaviraj Kanagaraj
Works at airCTO (A product by Launchyard)
kaviraj@launchyard.com
https://kaviraj.me
@kvrajk
* Agenda
- An example
- Goroutine leak
- Canceling Goroutine
- Context package (a little History)
- Context APIs
- An example
- Use cases
- Context usage inside standard library
* An example (1/2)
From [[https://www.youtube.com/watch?v=QDDwwePbDtw&t=1799s][Advanced Concurrency Patterns (2013)]] by Sameer Ajmani
.play using-context/pingpong/main.go /START/,/END/
* An example (2/2)
This code is clean and idiomatic. But there is a problem.
.play using-context/pingpong/main2.go /START/,/END/
* Goroutine leak (1/2)
*No* *Leak*
.play using-context/leaks/one.go /START/,/END/
*Leak*
.play using-context/leaks/two.go /START/,/END/
* Goroutine leak (2/2)
- goroutines gotten stuck trying to send/receive forever
- Unlike garbage variables, *leaked* *goroutines* are *not* *automatically* *collected*
- Could lead to running out of memory
- It is important to make sure that goroutines terminate themselves when no longer needed
* Canceling Goroutine (1/4)
Sometimes we need to inform a goroutine to stop what it is doing.
for example, in a web server doing some computation on behalf of a client that has disconnected
*But..*
- In Go, There is no way for one goroutine to terminate another *directly*
*Then* *How?*
By using two of the concurrency primitives, we are able to cleanup/terminate goroutines that no longer needed
- _select_ statement
- A specific _channel_ operation (guess?)
* select statement
Used to _multiplex_ channel send/receive operations
*General* *Form*
select {
case <-ch1:
// ...
case x := <-ch2:
// we can use received value
case ch3 <- y:
// ...
default:
// ...
}
- Like a switch statement, it has a number of cases and an optional _default_.
- Each case specifies a _communication_ (a send or receive operation on some channel) and an associated block of statement
* select statement
- A _select_ waits until a communication for some case is ready to proceed. It then performs that communication and executes the case's associated statements; *the* *other* *communications* *do* *not* *happen*
- _select_ with no cases, waits forever
select{}
* select statement - Countdown Example
From _The_ _Go_ _Programming_ _Launguage_ by K&D
.code using-context/select/counter.go /START/,/END/
* select statemnt - Buffered Channel Example
.play using-context/select/buffered.go /START/,/END/
If multiple cases are ready, _select_ picks one at random
* channels - A simple cheetsheet
ch := make(chan int) // Create
ch <- 7 // Send operation: Blocks till some other goroutine ready to receive
a := <-ch // Receive operation: Blocks till some other goroutine ready to send
close(ch) // Close
- Sending to a closed channel panics
- *Receiving* *from* *a* *closed* *channel* *returns* *zero* *value* *immediately* *(without* *blocking)*
- Closing a closed channel panics
- Sending to a _nil_ channel blocks forever
- Receiving from a _nil_ channel blocks forever
- Closing a _nil_ channel panics
* Canceling Goroutine (2/4)
Closing a channel can be used as a broadcast signal. Which can be used by other goroutines to terminate themselves.
*Lets* *cleanup* *our* *pingpong* *example* *using* *this* *technique!!*
* Canceling Goroutine (3/4)
.code using-context/pingpong/cleanedup.go /START/,/END/
.code using-context/pingpong/cleanedup.go /PLAYERSTART/,/PLAYEREND/
* Cancelling Goroutine (4/4)
.play using-context/pingpong/cleanedup.go /MAINSTART/,/MAINEND/
* Context package (a little history)
- Many Go APIs support cancelation and deadlines already.
- Different cancelation APIs in each package are a headache.
- Needed one that's independent of package or transport:
*Goal*: Provide a uniform cancelation API that works across package boundaries.
*2014*: golang.org/x/net/context - Sameer Ajmany
*2016*: brought into standard library as "context" package in Go1.7 - Brad Fitzpatrik
* Context API
*Types*
type CancelFunc
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
*APIs*
func Background() Context
func TODO() Context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
* Context API - Cancelation
*Two* *ways*
- context.WithCancel() - _cancels_ context explicitly by calling cancel()
- context.WithDeadline()/context.WithTimeout() - cancels context implicitly when _deadline_ is reached
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
* Context API - context.TODO()
- Use context.TODO when it's unclear which Context to use or it is not yet available.
- Never use _nil_ context. Use context.TODO instead
func SomeFunc(ctx context.Context, a Arg) {
... // do something with context
f(a)
}
func SomeOtherFunc(a Arg) {
SomeFunc(context.TODO(), a)
}
* Context API - context.Value()
func addUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
ctx := context.WithValue(req.Context(), "userid", req.Header.Get("userid"))
req = req.WithContext(ctx)
next.ServeHTTP(rw, req)
}
func useUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
uid := req.Context().Value("userid")
rw.Write([]byte(uid))
}
*NOTE*:
- Use context Values only for request-scoped data
- Do not use for passing optional parameters to functions.
- Make refactoring hard
- In short, Try not to use context.Value() (atleast, not more often)
[[https://peter.bourgon.org/blog/2016/07/11/context.html][https://peter.bourgon.org/blog/2016/07/11/context.html]]
* An Example - Lets context PingPong
.play using-context/pingpong/context.go /MAINSTART/,/MAINEND/
.code using-context/pingpong/context.go /PLAYERSTART/,/PLAYERTMPEND/
* Usecases
- In Go servers, when the request completes or times out, its work should be canceled.
- If need to access request-specific values (like user credentials)
- Database Query on multiple replica
- Network file system clients
- Cloud service clients
- *and* *more...*
* Context usage inside standard library
*os/exec*
- *exec.CommandContext()* - Kill the process (by calling os.Process.Kill) if the context becomes done before the command completes on its own.
*net*
- *net.DialContext()* - If the context expires before the connection is complete, an error is returned
*net/http*
- *request.WithContext()* - Returns a shallow copy of r with its context changed to ctx.
* Context usage inside standard library
.play using-context/std/http.go /MAINSTART/,/MAINEND/
* Context usage inside standard library (BONUS)
*database/sql* getting context in [[https://tip.golang.org/doc/go1.8#more_context][go 1.8]] (Feb, 2017)
- ExecContext(),
- PrepareContext(),
- QueryContext(),
- QueryRowContext(),
- PingContext(),
- StmtContext()
* Summary
- Goroutine leaks. Why does it matter?
- Cancelling goroutine using _select_ and channel _close_ operation
- Context package
- How to use them
* Reference
- [[https://blog.golang.org/pipelines][https://blog.golang.org/pipelines]]
- [[https://blog.golang.org/context][https://blog.golang.org/context]]
- [[https://golang.org/pkg/context][https://golang.org/pkg/context/]]
- [[http://golang.rakyll.org/leakingctx/][http://golang.rakyll.org/leakingctx/]]
- The Go Programming Language K&D book
- [[https://www.youtube.com/watch?v=f6kdp27TYZs][Go Concurrency Patterns by Rob Pike]]