-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathqueues.slide
More file actions
297 lines (172 loc) · 6.58 KB
/
queues.slide
File metadata and controls
297 lines (172 loc) · 6.58 KB
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
296
297
Queueing with Go
FIFO, Round-Robin and Beyond
24 Jan 2016
Loh Siu Yin
Technology Consultant, Beyond Broadcast LLP
siuyin@beyondbroadcast.com
* Queue Types / Ordering / Discipline
* First-In First-Out (FIFO)
Also known as First come, first served.
Useful in many day-to-day scenarios.
Eg. Buying food at a hawker stall.
* Last-In First-Out (LIFO)
Also known as a Stack.
Useful in picking the freshest item.
Eg. When buying fruit at the supermarket.
* Priority Queue
Like FIFO but has the ability to jump queue.
Eg. Everybody has to queue up.
But those who are in danger of missing their
flight will be served first.
* But first some operations
- Enqueue -- join the queue
- Dequeue -- remove from the queue so that the job can be served
- Head -- inspect (not remove) head of the queue
- Len -- number of items currently in the queue
* Let's define a Queue interface
.code q/queue.go /^package/,/^}/
* Let's also define a FIFO queue type
.code q/fifo.go /01 OMIT/,/02 OMIT/
Our FIFO queue internally (notice lower-cased s) has a slice of interface{}.
i.e. any data type -- because all data types satisfy the
empty interface.
* Provide methods for FIFO to statisfy the Queue interface
.code q/fifo.go /02 OMIT/,/03 OMIT/ HL01
* Let's do some testing
.code q/fifo_test.go /01 OMIT/,/02 OMIT/ HL01
Switch over to a terminal window and
go test -v -run Len
* Testing FIFO Enqueue and Dequeue (1)
.code q/fifo_test.go /04 OMIT/,/05 OMIT/ HL02
Note we can enqueue any data type Eg. 2 is an integer.
When we dequeue, we need a type assertion o.(int)
* Testing FIFO Enqueue and Dequeue (2)
.code q/fifo_test.go /05 OMIT/,/06 OMIT/ HL03
This tests the First-In First-Out behaviour
and also acceptance of interface{} .
* Handle some error conditions for our FIFO Queue
Any type with an Error() method returning a string
satisfies the go error interface.
.code q/fifo.go /03 OMIT/,/07 OMIT/ HL02
In our FIFO implementation:
.code q/fifo.go /05 OMIT/,/05a OMIT/ HL02
* You could just use errors.New
import "errors"
err := errors.New("something went wrong with the queue")
A matter of preference between using errors directly or
defining your own Errors type.
* A priority queue implementation
The go standard library package
container/heap
provides an implementation of a priority queue.
I leave this as an exercise for you.
* Multiple Queues
* Multiple Queue Servicing
Consider the situation where we have multiple queues
feeding into one server or a set of servers.
We often encounter this situation in *load*balancers*.
We can service multiple queues using:
- Round Robin
- Earliest Deadline First
- Shortest/Easiest Jobs First
- Fair Queueing
* Round Robin
Server(s) draws from each queue one at a time.
If a queue is empty it is skipped.
Similar to First-Come First-Served philosophy.
All queues get an equal chance of being serviced.
Thus it is fair in terms of the _number_ of jobs serviced.
_Long_ jobs have the same priority as _short_ jobs...
Imagine your job is to "ta-pau" one packet of chicken rice
while the two beside you are to "ta-pau" 10 and 20
packets of chicken rice respectively.
* Earliest Deadline First
The head element of all queues are scanned for their
individual deadlines.
The queue with the earliest deadline is serviced,
then the queues are scanned again.
Advantage: Last-minute jobs get done
Conversely jobs with long-term deadlines have to
wait a very long time to run.
* Shortest / Easiest Jobs first
The head element of all queues are scanned for their
estimated job length / difficulty.
The queue having the shortest job length / easiest complexity
is serviced,
then the queues are scanned again.
Advantage: Short / easy jobs get run often
Conversely Long / hard jobs may never get to run.
"ta-pau" one packet fast,
"ta-pau" 10 packets slow...
How can we ensure "fairness"?
* Fair Queueing
Principle is each queue will get a fair-share of resources.
Eg. If we have two queues:
(A) having 5 jobs of 2 minutes each
(B) having 2 jobs of 5 minutes each
Then both queues should empty / clear at about the same time.
Concepts:
- each job or queue item has a time cost and thus an estimated finish time
- each queue has a finish time = finish time of last job in the queue
* Psuedo-code on Wikipedia
.image fair_queue_wikipedia.png
* First try at an implementation
.code q/fair_q.go /01 OMIT/,/02 OMIT/
This implementation has N queues -- think Immigration Clearance at Airports.
Each queue is a FIFO.
And we keep a list of the last virtual finish time of a job in each of the queues.
i.e. the predicted / estimated time the latest job in a particular queue would complete
* Receiving a Job into the system
.image receive_wik.png
.code q/fair_q.go /02 OMIT/,/03 OMIT/ HL01
packet == Job
Notice I'm using the Queue interface's Enqueue method to enqueue job _j_.
Customize *chooseQueue* to your needs to maps incoming jobs to your
set of *N* queues.
* chooseQueue is a mapping from Job to queue number
In practice it could be based on customer name or id
or passport nationality
or some specific property of the Job
In my test implementation I choose to always map to queue number 1.
.code q/fair_q.go /03 OMIT/,/04 OMIT/
A real implementation would use a hashing function like
hash/fnv
available from the standard library.
* Updating estimated (virtual) finish time
.image update_time_wik.png
.code q/fair_q.go /04 OMIT/,/05 OMIT/ HL01
Finish time is greater of ( now and last job's finish time)
plus current job's time cost.
* Select and send a job to be processed
.image send_wik.png
.code q/fair_q.go /05 OMIT/,/06 OMIT/ HL01
The heart of the send function is selectQueue,
that is were the fair queueing comes in.
The rest of *send* is just a FIFO Dequeue.
* Fairly selectQueue
.image select_queue_wik.png
The main ideas:
- skip empty queues
- select the queue with earliest estimated finish time
This effectively is First-Come, First-Served weighted by the Job cost.
* selectQueue Go implementation
.code q/fair_q.go /06 OMIT/,/07 OMIT/ HL01
* Testing the Fair Queue - Update Time
.code q/fair_q_test.go /ut1 OMIT/,/ut2 OMIT/
FakeClock Now() is always 2000-01-01 00:00:00 UTC
* Testing Fair Queue send()
.code q/fair_q_test.go /01 OMIT/,/02 OMIT/
We have j1 and j2 in the queue and they should be
FIFO dequeued
* Testing the Fair Queue send() underflow
.code q/fair_q_test.go /02 OMIT/,/03 OMIT/
We have only j1 in the queue.
The second send() attempt should result in an
idle job.
* Running the tests
Switch to a terminal:
go test
* Presentation and code
.link http://github.com/siuyin/queue_present
Get the go present tool:
go get golang.org/x/tools/cmd/present