跟讀練習: Distributed Task Scheduler: System Design Interview (Stripe & Amazon Offers) - 通過影片學習英語口語
選擇練習模式
每種模式都會開啟全屏練習室。
324 句
如果句子過短或過長,請點選 Edit 進行調整。
1
in this video we are going to design a distributed task
2
scheduler like aws step functions a distributed task scheduler is a system
3
that is designed to execute arbitrary code
4
or trigger events at a specific time in the future either once
5
or on a recurring basis designing a robust task scheduler requires solving complex distributed systems problems including high precision
6
so a tasks should execute exactly when they are supposed to, maintaining fault tolerance so no tasks are lost if a worker node crashes,
7
and avoiding duplicate executions in a highly concurrent environment.
8
And so this is a really popular system design that is asked in interviews by Meta, Amazon, and Microsoft.
9
And they love to ask it because it exposes your understanding of distributed system trade -offs, which is a great way of differentiating the good candidates from the great ones.
10
For those of you
11
that are struggling with system design I highly recommend you check
12
out Tech Prep where you can see the full write -up
13
and the interactive AI whiteboard
14
that walks you through the design exactly like how an interviewer would
15
and it helped me go from projections to offers from the likes of Stripe and Amazon.
16
There are also a lot of system design concepts and patterns
17
that may be new to you in this system design and
18
so one way to keep up is to use the AI
19
highlighter where you can highlight anything in the write -up
20
and it will give you a full explanation based on the context of this system design.
21
So without further ado let's jump into the requirements.
22
So for the functional requirements firstly we will need task submission
23
so users can submit one -off delayed tasks or recurring tasks e .g cron expressions.
24
For task execution the system must execute the provided task payload so this could be a HTTP webhook, a script or a message push at the scheduled time.
25
In terms of task management users can query the state of a task so it can be pending,
26
running, completed, failed or cancelled and users should be able to cancel an upcoming task.
27
We should also then have an execution history
28
so the system must store the output and execution logs of completed tasks for auditing purposes.
29
Then for the non -functional requirements firstly scalability the system must handle 10 000 plus task submissions per second
30
and peak execution bursts of 50 000 tasks per second in
31
terms of high availability we'll want 99 .99 uptime for task submission
32
and execution routing and then for reliability we want no data loss
33
so once the task is acknowledged as accepted it must be durably stored
34
and guaranteed to execute and then finally in terms of precision
35
and latency tasks should begin execution within 200 milliseconds of their scheduled time.
36
So now that we've looked at the requirements let's look at the data model we need to build to fulfill those requirements.
37
So to handle the contrasting demand of strict state management
38
and the massive write -heavy logging I think a polyglot persistence strategy is required for a system of this scale
39
and so what polyglot persistence simply means is
40
that we have to use different data storage types for different things and I'm going to walk through each of them now.
41
So the first thing will be our relational database which you can implement with Postgres.
42
So this will serve as the system's source of truth for task definitions, schedules and metadata and we rely heavily on its asset properties
43
and row level locking to manage the state machine of a task without race conditions.
44
And the core entities will include a task definition
45
so this is the core configuration object it stores the user's
46
intent the payload to be executed for example http webhook url
47
and body the retry policy and the scheduling logic it's either one off
48
or a cron expression next will be our task execution
49
so this represents a single distinct run of a task definition
50
and it acts as the primary state machine
51
so we'll go from pending to queued to running to either success
52
or failed and it includes an item potency key and tracks exactly
53
when the task was picked up by a worker and when it finished.
54
So this is really important and is the kind of core of our architecture.
55
Next we'll also need an in -memory store which we can implement with Redis and this is also super important.
56
So this will act as the high speed timer and scheduling buffer
57
and we will also utilize Redis sorted sets
58
so z sets where the score is in the epoch execution time
59
and this will allow the system to query for tasks
60
that need to run right now in o log n time bypassing the need to constantly scan the relational database.
61
Now if you don't know what red assorted sets are don't worry about
62
that we're going to explain those in detail later on.
63
We'll also need a message broker so something implemented with either Kafka
64
or RabbitMQ which will serve as the durable execution queue and
65
so it decouples the scheduling components from the worker pool allowing the system to absorb massive spikes in task executions
66
so back pressure without overwhelming the database
67
and finally we'll also need a wide column store
68
so something like Cassandra and this is used exclusively for execution logs
69
and history since a single recurring task might generate millions of
70
logs over its lifetime a high write throughput horizontally scalable no sql database is ideal for this
71
and so that's the data model so
72
if you understand these four data storage techniques
73
and why they're used you will be in a really good place to move on to our api design
74
so for a distributed task scheduler we can just use a classic restful API to interact with the data.
75
RESTful API is simple, widely used, stateless and they support caching which makes it a great candidate for our system.
76
So the first endpoint can be a create task endpoint.
77
So this will be a post to a v1 tasks.
78
It accepts a payload, execution time or a cron and a target and will return a task ID.
79
Next will be the get task status.
80
This will be a get request to the v1 tasks task id
81
and it will return the current state and metadata of that task.
82
A cancel task endpoint so this will be a delete at v1 tasks task id
83
and it marks a pending task as cancelled
84
and then finally for logs we can get a get logs endpoint
85
which will be a get to a v1 tasks task id executions
86
and this will return a paginated execution history of that task.
87
Obviously production system will have a lot more endpoints
88
but these are the kind of core endpoints to outline to your interviewer what's kind of core to this system
89
and what we'll need to cover as we go through our high -level system design.
90
So now that we've mentioned our data model and API design let's look at our high -level design.
91
So at a high level the system architecture can be separated in terms of accepting a task as well as executing it.
92
So the first part will be the submission path.
93
So this path focuses on securely receiving the task and durably persisting its schedule.
94
So here a client will upload it'll go to the gateway
95
this will route the incoming request it'll also authenticate the client
96
and enforce rate limits this will then get pushed to our task management service
97
which validates the payload and cron expressions
98
and it writes the task definition to the task database again Postgres with this state of pending and then
99
if the task is scheduled to run in the very near
100
future e .g within the next 15 minutes the service could
101
also push a lightweight reference to the task directly into the redis zset timer next is the execution path
102
and so this path focuses on time triggering the task
103
and hand and handing it off to worker nodes
104
so firstly we'll have relay nodes this will be a background batch process
105
that periodically queries the task database postgres for tasks scheduled to run in the near future for example 10 minutes
106
and stages them in the redis zset timer this prevents the database from being continuously hammered by high frequency polling
107
and then what we can have is we can have a fleet of lightweight scheduler nodes
108
which will continuously run a process let's say every 100 milliseconds
109
to pull the redis z set for tasks whose scheduled time is less than
110
or equal to the current time once we've pulled those tasks
111
off the redis z set we can then add them to a task queue
112
and so the task payload is fetched
113
and pushed onto this highly available message queue
114
and to prevent noisy neighbors where one customer submitting 50 000 tasks saturates the queue
115
and delays others tasks are rooted to tenant specific queues using a hash of the tenant id
116
and this allows workers to round robin between tenant queues ensuring
117
equitable compute distribution among our customers next we'll have our worker nodes
118
and so this is a fleet of horizontally scaled compute nodes
119
that consume messages from the queue execute the business logic eg
120
make the request to the target system the http webhook
121
and then report the successor failure to the task database
122
and then keep a historical log in cassandra
123
so pretty simple system it's not too crazy and so
124
while this high level design does separate our concerns it does contain critical flaws
125
so pulling our database efficiently we can dive deeper into
126
that workers can crash mid -execution leaving tasks stuck in limbo
127
which we'll have to deal with
128
and network retries could cause a payment task to execute twice
129
and so we have to deep diaper into all of these and show how we can resolve them.
130
So firstly let's look at Redis Z sets and how they're implemented so we have a better understanding.
131
So Redis sorted sets is an in -memory data structure that automatically orders unique elements by a specific score.
132
In this case the Unix execution timestamp allowing the scheduler to instantly identify
133
and extract tasks run in o log n time without constantly polling
134
and overwhelming our primary database and
135
so under the hood the way it's implemented is two data structures the first one is a hash map
136
and this is used for o1 point lookups
137
so the purpose of this is to get
138
that instant access via task id and
139
so the situation this would be used is like
140
if a user wants to cancel an upcoming task
141
or the system needs to check its exact scheduled time can check via the task
142
id to get its execution timestamp
143
and redis ignores time -based sorting entirely by consulting this internal hash map to find by key
144
and instantly return the execution timestamp in o of one time
145
so it doesn't have to traverse any list the second data structure is the skip list
146
and this is for o log n time -based polling and
147
so here this is the purpose of this is for fast traversal by execution timestamp.
148
And so the scenario we use this is like the schedule node polls for tasks ready to execute right now.
149
So we'll check for tasks that's execution time is less than or equal to right now.
150
And so obviously Redis cannot use the hash map here because it doesn't know which specific task IDs are right for execution.
151
Instead it goes to this skip list. And
152
so it uses the upper level express lanes to quickly skip
153
over millions of tasks scheduled far into the future like task C
154
and it drops down to the base level only
155
when it nears the target timestamp landing directly on the threshold of ready tasks to instantly extract them for the message queue.
156
So if we look at a quick example let's say we're polling for ready tasks
157
and the current timestamp is this one here on the screen.
158
Okay well the first thing we're going to do is start at level three start at our head
159
and then look right and when we look right we can see we have task c
160
which as you can see is greater than our current time stamp as we can see that's in the future
161
so we can ignore it so what do we do
162
so we drop down to the next level
163
and again we start out our head and we look to the right
164
and as you can see we move across
165
and we can see the time stamp is exactly equal to our current time stamp
166
and so therefore we know we have found our upper bound
167
and so what we now need to do is find all the elements up to
168
that upper bound and the way we do
169
that is drop down again a level and
170
because we're at the bottom level here we know this is a perfectly sorted link list
171
and so redis instantly knows
172
that any node from the head to task b is right for execution
173
so it sequentially traverses the level one extracting task a
174
and task b and returning them to the scheduler to be pushed to the kafka queue
175
so hopefully that makes sense
176
and again the primary architectural benefit here is we are able
177
to extract ready to run tasks in o log n time without constantly polling and overwhelming the primary database.
178
So next we need a look at high precision scheduling.
179
So running a select all from the tasks table in our
180
Postgres database containing billions of rows every second will instantly melt the database.
181
We need a way to track time efficiently to achieve that less than 200 milliseconds execution precision without database bottlenecking.
182
And so we will implement a push -pull time partitioned architecture.
183
So the first step, again, we have our luck ahead cron.
184
So this background relay worker will wake up every five minutes
185
and it'll query the Postgres database for all tasks scheduled to run in the next 10 minutes.
186
Then we'll have our Redis sorted set.
187
The relay will push these upcoming task IDs into the Redis Z set.
188
And the score in the Z set again is that exact Unix epoch timestamp of when the task should run.
189
Then we will have sub second polling.
190
So a fleet of lightweight scheduler nodes will pull the Redis Z set every 100 milliseconds.
191
looking for tasks where the score is less than or equal to the current timestamp.
192
Then we will have an atomic handoff, so using a Lua script.
193
So to prevent multiple scheduler nodes from pulling the exact same task during concurrent polling, we use a custom Lua script.
194
And because Redis executes Lua scripts as a single indivisible transaction on its single threaded event loop, it guarantees an atomic check and pop.
195
And so this means that the script queries for the ready tasks and immediately deletes them,
196
safely handing the tasks to exactly one scheduler to then push onto the message queue.
197
And then finally, to mitigate thundering herds.
198
So, for example, humans will naturally schedule cron jobs at clean intervals, for example, every midnight.
199
And so to prevent massive artificial latency spikes at the top of the hour, the schedule can inject optional execution jitter,
200
which will add a random number of seconds between, let's say, 0 and 300 delay for tasks
201
that do not require strict millisecond precision so that we can smooth out that load curve,
202
ensuring that we don't have massive spikes in executions.
203
And so the architectural benefit here is the heavy relational database is only queried sequentially in predictable batches,
204
while the high frequency sub -second polling and lock frequency concurrency control is handled entirely in memory by Redis.
205
Next, we need to look at our worker fault tolerance.
206
So a major distributed system problem is the stuck task.
207
So what happens if a worker node pulls a critical task from the queue, but the server suffers a hard crash before finishing?
208
Well, the task must not be lost, nor can it be duplicated prematurely.
209
So to guarantee at least once delivery and fault tolerance,
210
we rely on visibility timeouts and explicit acknowledgements natively supported by brokers like AWS SQS or RabbitMQ.
211
what we need is
212
when a worker pulls a message the broker does not delete
213
it instead it will update the metadata attribute on the message
214
for example we could have a field invisible underscore until timestamp and so
215
when other workers pull the queue the broker's internal query simply
216
filters out messages where the invisible until is greater than the current time
217
because that is currently being executed
218
and we should not touch it we'll also have the receipt handle
219
and so upon pulling the message the broker also gives the worker a unique temporary identifier
220
so this receipt handle or delivery tag and we'll see why that's important in just a second.
221
We'll also have heartbeats so this is the visibility extension so
222
if a task naturally takes a long time a background thread on the worker periodically pings the broker to overwrite
223
and extend the invisible until timestamp actively maintaining its exclusive lock and preventing other workers from touching it.
224
We'll also have the ACK which is the final deletion and
225
so upon successful completion the worker sends the receipt handle
226
which we saw previously back to the broker in an ACK request
227
and the broker uses this handle to locate the message in O of one time
228
and permanently hard delete it from its memory or disk so that it is never picked up again by any other worker.
229
And then in the case of handling the crash well
230
if a worker crashes it stops sending the heartbeats
231
and never sends the ACK and so once the invisible until timestamp expires
232
and it's in the past the message naturally becomes visible again for a healthy worker node to claim
233
and process and so this is a really nice way of handling
234
that worker fault tolerance that most candidates won't be able to answer.
235
Next we need to look at execution guarantees and
236
because our fault tolerance mechanism relies on timeouts
237
and retries we guarantee at least once execution however this means
238
that a task might run twice and
239
so strict exactly once delivery is theoretically impossible over an unreliable
240
network therefore we must implement idempotency to make duplicate executions harmless
241
so we need optimistic concurrency control
242
so before a worker executes a payload it attempts to update the task status in postgres
243
so you'll update the status to running
244
and increment its version number by one right and so this means
245
if the worker crashed previously
246
and a duplicate worker picks up the same task the database will reject the second update
247
because the status is no longer pending
248
and the worker knows to safely discard the duplicate and again
249
that version number acts as a unique constraint
250
so once one worker increments it from one to two the second worker's update fails
251
because it will still be looking for version one and
252
so this implements the optimistic locking ensuring exactly one worker can claim a task even under high concurrency
253
and the version number means the check and update operation truly is atomic
254
and we'll also need client side item potency keys
255
so for HTTP webhook tasks the scheduler will inject an item
256
potency key header generated from the task id plus the execution timestamp into the outbound request
257
and the downstream system receiving the webhook is responsible for using this key to ignore duplicate HTTP calls
258
and so by using these mechanisms we are improving our execution guarantees.
259
And then finally looking at scaling our storage layer
260
because handling 10 ,000 plus continuous task commissions per second pushes the limits of single node architectures.
261
This translates to 10 ,000 plus database inserts operations per second plus B3 index updates all
262
while serving continuous select queries from the relay components.
263
And so to prevent severe CPU and disk IO bottlenecks both the relational database
264
and the in -memory Redis cluster must be horizontally partitioned
265
so firstly looking at postgres you know
266
while a heavily provisioned single postgres primary can technically peak at 10 000 tps running it at
267
that threshold and production guarantees latency spikes and lock contention and
268
so firstly we'll need horizontal sharding
269
so we distribute the right load across multiple independent postgresql clusters
270
for a multi -tenant sas platform we shard based on tenant id
271
and this ensures data locality
272
so all tasks for specific customer reside on the same database node keeping transactions highly localized
273
and by provisioning 10 distinct nodes the load drops to a highly manageable 1000 tps per primary
274
and then we'll also have time -based table partitioning
275
so as the system scales to billions of executed tasks the
276
active database shard will become bloated slowing down the look ahead cron queries
277
and so we can mitigate this by partitioning the task tables by month
278
or week and
279
so this allows the system to quickly drop historical partitions archiving
280
them to cold storage like s3 without running expensive delete queries and keeping the active b3 index incredibly small and fast
281
next looking at redis and so
282
because redis operates entirely in memory a single instance can handle
283
100 000 simple operations per second however a single node creates a single point of failure
284
and a massive single sorted set degrades performance and
285
so we can see time complexity duration so because we rely on zadd
286
and z range by score queries both of
287
which operate o log n time as a single z set
288
grows to millions of elements the single threaded redis event loop will begin to block
289
and so therefore we will need time bucketed keys
290
so instead of one global schedule task zset we can partition
291
zsets by time bucket for example tasks minute you know 2 30
292
and tasks minute 2 31 and as well as
293
that we can also have a hash slot distribution
294
so by deploying a redis cluster these time bucketed keys are deterministically hashed
295
and distributed across redis primary nodes and this horizontally scales both the memory footprint
296
and the olog n compute overhead ensuring this layer never blocks under massive concurrency
297
and so look we've gone deep on you know high precision scheduling worker fault tolerance execution guarantees
298
and scaling the storage layer so
299
if you need know these things these are the core things interviewers are going to ask you
300
so knowing them in depth will really bulletproof you
301
when you go into your interview and
302
so to finish up let's just walk through the complete architecture one last time
303
so you've kind of got that one mental snapshot of understanding how everything works together
304
so a client will submit a payload to the API gateway
305
that will then get routed to the task management service which writes
306
that persistent configuration to Postgres the relay service every few minutes
307
in a batch process will pull tasks scheduled for the near future from Postgres
308
and stage them to our Redis Z set keyed by their
309
precise execution epoch time high frequency scheduler nodes then continuously pull Redis
310
and when a task's time arrives a Lua script atomically pops it from redis
311
and pushes it onto a partitioned kafka
312
or rabbit mqq utilizing tenant -based routing to avoid noisy neighbors
313
a worker node claims a task triggering a visibility timeout the
314
worker then updates the database state to running using optimistic locking
315
to prevent concurrent execution races the worker then executes the arbitrary code
316
or http webhook injecting an item potency key to protect downstream systems from network retries
317
and the worker then sends an act to the queue to permanently delete the message updates the state
318
and postgres to complete it
319
and flushes the execution logs asynchronously into the cassandra wide column
320
store for permanent auditing to check out the full write -up
321
and the ai whiteboard head over to techprep .app
322
and hopefully you got some value out of this
323
if you did please like and subscribe
324
and share with a friend it helps the channel out a lot and i will see you in the next one
同頻道
FOR YOU
推薦影片
本課的詞彙與口語要點
這節 C1 級別的口語課以影片“Distributed Task Scheduler: System Design Interview”為素材。 影片中反覆出現的詞有:task, execution, worker, redis, database。 這段影片共有 324 個句子、4079 個單詞可供跟讀。 講話部分時長為 22:56。 說話人語速自然,每分鐘約 178 個詞,接近日常對話。 只有 73% 的單詞屬於英語最常用的 3,000 詞,詞彙難度較高。
影片中的重點詞彙
影片中最難的 15 個單詞,附發音和釋義:
| 單詞 | 發音 | 釋義 |
|---|---|---|
| node 名詞 | /noʊd/ | 結節 /結節 |
| queue 名詞 | /kju/ | 佇列 /佇列 |
| execute 動詞 | /ˈɛksɪˌkjuːt/ | 處決 /處決, 處死 /處死 |
| timestamp 名詞 | /ˈtaɪmˌstæmp/ | 時間戳 /時間戳, 時戳 /時戳 |
| query 名詞 | /ˈkwɪɹ.i/ | 問題 /問題, 質問 /質問 |
| payload 名詞 | /ˈpeɪloʊd/ | 裝載量 /裝載量, 載荷 /載荷 |
| distribute 動詞 | /dɪˈstɹɪbjuːt/ | 分配 |
| endpoint 名詞 | 端點 /端點 | |
| firstly 副詞 | /ˈfɜɹstli/ | 第一, 首先 |
| tenant 名詞 | /ˈtɛ.nənt/ | 房客 |
| broker 名詞 | /ˈbɹoʊkɚ/ | 經紀人 /經紀人, 中間人 /中間人 |
| precision 名詞 | /pɹɪˈsɪʒ.ən/ | 精度, 準確度 /準確度 |
| tolerance 名詞 | /ˈtɑ.lə.ɹəns/ | 寬容 /寬容 |
| duplicate 動詞 | /ˈd(j)uː.plɪˌkeɪt/ | 複製 /複製 |
| hash 名詞 | /ˈhæʃ/ | 井號 /井號, 井字 |
影片中出現的短語動詞
| 單詞 | 發音 | 釋義 |
|---|---|---|
| pick up 動詞 | 拾取, 撿 /撿 | |
| check out 動詞 | 退房 | |
| go through 動詞 | 通過 /通過 | |
| slow down 動詞 | 減速 /減速, 放慢 | |
| wake up 動詞 | /ˌweɪk ˈʌp/ | 醒, 醒來 /醒來 |
影片中的語法
說話人最常用的結構,並附上影片中的原話:
| 結構 | 影片中的用法 |
|---|---|
| 被動語態 be + 過去分詞 — 強調發生了什麼,而不是誰做的 | is designed · are lost · is asked |
| 現在完成時 have/has + 過去分詞 — 過去發生但與現在仍有關聯的事 | we've looked · we've mentioned · we've pulled |
| 定語從句 who / which + 從句 — 補充說明人或事物 | offs, which is · RabbitMQ which will · caching which makes |
需要注意的發音
說話人用了 40 次縮略和弱讀形式,例如 we'll, we're, we've。請按聽到的簡短形式來說。
- “sh” 和 “zh” 音: scheduler /ˈʃɛdjələ/, precision /pɹɪˈsɪʒ.ən/, hash /ˈhæʃ/, partition /pɑɹˈtɪ.ʃən/, submission /səbˈmɪʃən/
- 長單詞——注意重音位置: concurrency /kəŋˈkʌɹənsi/, millisecond /ˈmɪlɪˌsɛkənd/, invisible /ɪnˈvɪzəb(ə)l/, metadata /ˈmɛt.əˌdeɪ.tə/, visibility /ˌvɪz.əˈbɪl.ɪ.ti/
中文母語者容易讀錯的音:
- /v/ — 不要讀成 /w/,上齒輕觸下唇: invisible /ɪnˈvɪzəb(ə)l/, overwhelm /ˌoʊ.vɚˈwɛlm/, visibility /ˌvɪz.əˈbɪl.ɪ.ti/, interviewer /ˈɪntɚvjuɚ/
- 詞尾子音 — 要讀清楚,後面不要加母音: node /noʊd/, execute /ˈɛksɪˌkjuːt/, timestamp /ˈtaɪmˌstæmp/, payload /ˈpeɪloʊd/, distribute /dɪˈstɹɪbjuːt/
- /r/ 和 /l/ 的區別: firstly /ˈfɜɹstli/, tolerance /ˈtɑ.lə.ɹəns/, relay /ˌɹiˈleɪ/, restful /ˈɹɛs(t)fʊl/, architectural /ˌɑɹkɪˈtɛkt͡ʃəɹəl/
如何用這段影片練習
- 先完整聽一遍影片,不要開口,記下不認識的單詞。
- 先用 0.75 倍速逐句跟讀,熟練之後再回到正常速度。
- 錄下自己的聲音並與原聲對比,特別注意 node, queue, execute 這類單詞。
什麼是跟讀法?
跟讀法 (Shadowing) 是一種有科學依據的語言學習技巧,最初開發用於專業口譯員的培訓,並由多語言者Alexander Arguelles博士普及。這個方法簡單而強大:您在聽英語母語原聲的同時立即大聲重複——就像是一個延遲1-2秒緊跟說話者的影子。與被動聽力或語法練習不同,跟讀法強迫您的大腦和口腔肌肉同時處理並模模擬實的講話模式。研究表明它能顯著提高發音準確性,語調,節奏,連讀,聽力理解和口語流利度——使其成為雅思口語備考和真實英語交流最有效的方法之一。








![[2026] TOEFL iBT Speaking Take an Interview | Step-by-Step Strategies #0008](https://img.youtube.com/vi/Bo4ARkCDQLk/mqdefault.jpg)




