When DSA Solved Our AWS Scaling Problem
In the age of AI, it feels like DSA is slowly disappearing from day-to-day development.
You can ask an AI to generate a queue, configure an AWS service, or write a retry mechanism in seconds.
But there is one thing AI cannot remove from engineering:
Constraints.
And when constraints appear, DSA suddenly becomes relevant again.
At our startup, we often solve problems in a scalable way. But scalability doesn't always mean adding another AWS service. Sometimes, the solution is simply using the existing components differently.
You usually don't recognize a DSA concept in a real-world system until someone asks:
"Why are we doing it this way?"
That question led us to a surprisingly simple solution to an AWS scheduling problem.
The Problem: We Were Running Out of Schedules
Imagine we have a large number of jobs.
Each job can fail and needs to be retried on subsequent days:
Parent Job
│
├── Retry 1 → +1 day
├── Retry 2 → +2 days
└── Retry 3 → +3 days
Initially, this seems straightforward.
We can simply create an EventBridge schedule for every retry.
But then scale enters the picture.
We encountered a situation where, for every 6,000 jobs, we needed to create 3 separate EventBridge schedules.
That means:
6,000 jobs
×
3 retry schedules
=
18,000 schedules
Now imagine increasing the workload.
What happens if we have 10,000 jobs?
10,000 jobs
×
3 retries
=
30,000 schedules
And what if this keeps growing?
We eventually run into EventBridge scheduling limits.
The obvious solution might be:
"Let's add another AWS component."
But our senior asked a different question:
"Can we solve this using a sliding window?"
That was the interesting part.
First, What Is a Sliding Window?
A sliding window is a technique where we don't process the entire dataset at once.
Instead, we maintain a smaller active window and continuously move it forward.
For example, imagine these jobs:
Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7
├───────┼───────┼───────┼───────┼───────┼───────┼───────┤
[──────────── Window ────────────]
As time moves forward:
Before:
Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7
[──────── Window ────────]
After:
Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7
[──────── Window ────────]
The important idea is:
We don't need to keep scheduling everything forever.
We only care about jobs relevant to the current time window.
That observation changes how we think about the EventBridge problem.
What If We Don't Schedule Every Retry?
This was the key idea.
Instead of asking:
"When should this retry happen?"
and creating an EventBridge schedule for that future date...
we ask:
"Which retries are supposed to happen today?"
That small change removes the need to schedule every future retry individually.
The Original Approach
Let's say today is August 18.
A parent job runs today.
Its retries are:
August 18
│
├── Retry 1 → August 19
├── Retry 2 → August 20
└── Retry 3 → August 21
The traditional approach is:
EventBridge
│
┌──────────┼──────────┐
▼ ▼ ▼
Aug 19 Aug 20 Aug 21
Retry 1 Retry 2 Retry 3
For every parent job, we create future schedules.
This is where the number of schedules grows rapidly.
The Sliding Window Approach
Now let's change the perspective.
Instead of scheduling:
Retry 1 → tomorrow
Retry 2 → day after tomorrow
Retry 3 → three days later
we maintain a daily execution window.
Every day, we ask:
"Which existing parent jobs have a retry due today?"
Then we execute those retries.
TODAY
│
▼
┌─────────────────┐
│ Sliding │
│ Window │
└────────┬────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Retry 1 Retry 2 Retry 3
due today due today due today
EventBridge no longer needs to know about every individual retry.
It only needs to trigger the parent job.
But What About Jobs From Previous Days?
This is where the idea becomes interesting.
Suppose a parent job ran 3 days ago.
Let's say:
Parent Job
August 15
│
├── Retry 1 → August 16
├── Retry 2 → August 17
└── Retry 3 → August 18
Today is August 18.
The third retry is due today.
Under the traditional approach, we would have created a schedule on August 15 for the retry that should execute on August 18.
But we don't need to do that anymore.
Instead, today's sliding window discovers it.
August 15 August 16 August 17 August 18
Parent Job Retry 1 Retry 2 Retry 3
│ │ │ │
└────────────────┴────────────────┴────────────────┘
▲
│
TODAY
The system simply asks:
"What retries are due on August 18?"
And finds:
Parent Job: August 15
Retry: 3
Due: August 18
Then it executes it.
The Trick
This is the entire trick:
Don't schedule the future retries.
Instead Conceptually:
┌─────────────────────┐
│ Daily Trigger │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Current Time │
│ Window │
└──────────┬──────────┘
│
▼
┌──────────────────────────┐
│ Find retries due today │
└────────────┬─────────────┘
│
┌─────────┴─────────┐
▼ ▼
Retry due Not due
│ │
▼ ▼
Execute Ignore
Why This Scales Better
The important difference is what we are scheduling.
Number of schedules grows
with the number of jobs
Now the number of EventBridge schedules is no longer directly proportional to:
Number of Jobs × Number of Retries
Instead, EventBridge becomes the mechanism that wakes up the scheduler/processor, while the application determines which work actually needs to happen.
The Bigger Lesson
The interesting part of this problem wasn't really EventBridge.
It was recognizing that we were using EventBridge to solve a problem that didn't necessarily belong there.
We initially thought:
"We need to schedule millions of future events."
The better question was:
"Do we actually need to schedule millions of future events?"
And the answer was no.
We only needed to know:
"What needs to happen today?"
That is where a simple DSA concept like sliding window becomes a practical system-design technique.
DSA Is Not Dead
This is why I don't think DSA is disappearing from software engineering.
Sometimes the answer is a sophisticated distributed system.
And sometimes...
it's a sliding window.
The important skill isn't memorizing DSA patterns.
Top comments (0)