DEV Community

Cover image for When DSA Solved Our AWS Scaling Problem
Girish bari
Girish bari

Posted on

When DSA Solved Our AWS Scaling Problem

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now imagine increasing the workload.

What happens if we have 10,000 jobs?

10,000 jobs
    ×
3 retries
    =
30,000 schedules
Enter fullscreen mode Exit fullscreen mode

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 ────────────]
Enter fullscreen mode Exit fullscreen mode

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 ────────]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The traditional approach is:

              EventBridge
                  │
       ┌──────────┼──────────┐
       ▼          ▼          ▼
    Aug 19     Aug 20     Aug 21
    Retry 1    Retry 2    Retry 3
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The system simply asks:

"What retries are due on August 18?"
Enter fullscreen mode Exit fullscreen mode

And finds:

Parent Job: August 15
Retry:      3
Due:        August 18
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)