# Eleven departments, one afternoon: how UCAM Cloud ran UIU's Fall 2026 section selection

> UIU's Fall 2026 section selection on UCAM Cloud: eleven departments in one window for the first time, 7 million requests with 8 server errors, a 114 ms p99, 29,047 course seats placed and none over capacity.

- Author: [Azraf Al Monzim](https://monzim.com/)
- Published: 2026-10-25
- Topic: Distributed systems
- Tags: ucam-cloud, scale, reliability, performance, observability
- Canonical: https://monzim.com/blog/ucam-cloud-fall26-section-selection

---

**UCAM Cloud** is the course-registration and section-selection platform of [United International University](https://www.uiu.ac.bd/) (UIU) in Dhaka. On Saturday, 24 October, UIU ran section selection for the Fall 2026 trimester on it. For the first time, **all eleven departments selected in the same window**. Every department opened at 10:00, with CSE starting with its priority students, and CSE's regular students, the biggest group of the day, followed at 13:00.

It placed more course seats than any selection day before it, and it was the smoothest. Here is the result first.

## Key takeaways

- **Scale:** eleven departments in one window, a first for UCAM Cloud.
- **Reliability:** 7,000,333 API requests over the day with 8 server errors (0.00011%).
- **Speed:** a p99 of 114 ms for the whole day; each selection took 32 ms to process.
- **Students:** a typical student in the 10:00 rush placed four courses in 1 minute 23 seconds.
- **Correctness:** 29,047 course seats placed, and no section ever went over capacity.
- **Next:** the live seat board fell behind at the 13:00 peak without affecting selection, and is being rewritten on Erlang/Elixir.

_Chart: Fall 2026 selection day at a glance: 7.0 million API requests over the day, 8 server errors, a day p99 of 114 ms, 29,047 course seats placed, no section above capacity, and eleven departments in one window._

_Measured from CloudWatch and the function logs, 24 Oct 2026, Dhaka time._

_Chart: Three ratios from the day: 93.4% of students who pressed Select got at least one section on the first day; 99.9999% of requests were answered without a server error; 100% of sections stayed within capacity._

_The three numbers that matter to a student: did I get in, did it work, was my seat real._

Students didn't wait hours, didn't fight a frozen page, and not a single complaint about selection reached us. This post is how that day went, what we built to get there, and the one part of the system that has to grow before the next one.

## Why selection day is hard

**Section selection** is the step where each student picks a section, a fixed class time with a faculty member, for every course they were advised to take. Every section has a fixed number of seats, and they go to whoever claims them first.

Selection day is like a flood you know the date of. For most of the trimester the platform is quiet. Then, at a time everyone can see on the notice board, thousands of students press the same button in the same second, because a seat in the section they want is on the line.

This time the flood came in two waves, and it covered more of the university than ever before:

_Chart: Departments opening on 24 October: all eleven departments light up at 10:00, CSE with its priority students; at 13:00 CSE lights up again as its regular students join._

_Eleven departments, one window. Each tile is a department; the last one lights up twice._

Before UCAM Cloud, a day like this meant five to six hours of refreshing a portal that kept falling over. The goal for Fall 2026 was simple to say and hard to do: every department, one window, and every student done in minutes, not hours.

## What we built for this window

Most of the work since Summer 2026 went into making the day fair and making it correct, not just fast.

- **One active tab per student.** Selection works in a single tab at a time. Ten open tabs no longer give anyone ten chances at the same seat.
- **Protection against automation** on the course faculty list and the section selection pages. A seat goes to a student pressing Select, not to a macro or a script.
- **Nothing changes under a student's feet.** Syncs from the university's system pause for a department while its window is open, and a selection can't move once the university's system is registering it.
- **Every window is prepared automatically** before it opens, can be rehearsed with a dry run, and registration runs only inside a live window.
- **A cross-check against the university's system**, per department and per student, plus a backup and restore for each department's selections.
- **A fair limit per student** on how fast seat requests can be sent, and roles and permissions so each department's staff manage their own students.

All of it was switched on for every department on 24 October.

## The day, hour by hour

_Chart: Timeline of 24 October: the window was prepared automatically in the morning; at 10:00 all eleven departments opened, CSE for its priority students, and the backend reached 3,194 calls in a second; at 13:00 CSE's regular students joined; at 13:00:02 the day's peak of 5,191 calls in one second; at 13:00:05, 389 seats were granted in one second; by 14:00 traffic had settled; by midnight the platform had answered 7.0 million requests with 8 server errors._

_All times Dhaka (UTC+6)._

## The shape of the day

_Chart: API requests per minute from 09:00 to 20:00 on 24 October: a sharp spike at 10:00 to 93,475 requests a minute, a quiet middle, then the day's peak at 13:00 with 147,773 requests in one minute, halving within two minutes and tailing off through the afternoon._

_API requests per minute, 09:00–20:00: all departments open at 10:00, CSE regular students at 13:00. Hover or tap the chart to read any minute._

Two cliffs, not two hills. At 10:00 the platform went from about 590 requests a second to 1,558 a second, averaged over the minute. At 13:00 it took **147,773 requests in sixty seconds**, about 2,463 a second, and was back to half that within two minutes. Over the busiest clock hour, 13:00 to 14:00, it answered 1.95 million requests.

The part I'm proudest of is what didn't move. **p99 latency** is the time within which 99% of requests are answered, so it tracks the slowest 1% of responses, the ones students notice. Here it is minute by minute, over the same eleven hours:

_Chart: p99 response time per minute from 09:00 to 20:00, on a scale up to one second: it stays between 54 and 161 ms all day, with no visible change at the 10:00 or 13:00 openings._

_p99 latency per minute at the API, on a one-second scale. The traffic chart is two cliffs; this one is a flat line._

Even in the minute 147,773 requests arrived, 99% of them were answered in under a fifth of a second. Across the whole day the p99 was **114 ms**, and the median was 17 ms.

## The first seconds at 13:00

A minute is too coarse to see what really happens at an opening, so here it is per second, counted from every log line rather than sampled.

_Chart: Backend calls per second from 12:59:30 to 13:01:30: about 1,100 to 1,400 a second before the window opens, then 2,422 at 13:00:00, 4,800 at 13:00:01 and a peak of 5,191 at 13:00:02, easing to about 3,000 by 13:00:05 and 1,500 by the end of the first minute._

_Backend calls per second around the 13:00 opening, from the function logs._

Before 13:00 the line is already at 1,100–1,400 calls a second: thousands of students waiting on the selection screen. Then the clock ticks over. **5,191 calls at 13:00:02**, and every one of them answered.

The seat requests themselves arrive a beat later, once the page knows the window is open:

_Chart: Seat requests per second from 12:59:50 to 13:01:30, split into seats taken and requests refused: nothing before 13:00:01; seats taken climb to a peak of 389 a second at 13:00:05, with refusals growing as popular sections fill, then both ease over the first minute._

_Seat requests per second around 13:00. Blue: a seat was taken. Grey: a rule said no, almost always because the section was already full._

At 13:00:05 the platform granted **389 seats in one second**. In the first three seconds almost every request got a seat. By the tenth second some sections were full, and the grey band grows. That grey band isn't failure. It's the system telling the truth about a section that just filled.

## How long did a student take?

Throughput is the platform's view. A student's view is simpler: how long from the moment the window opens until my courses are placed?

_Chart: Per-student timing on 24 October: half of students made their first selection within 10 seconds of the opening; a typical student in the 10:00 rush placed all four courses in 1 minute 23 seconds; a typical CSE regular student at 13:00 placed four courses in 4 minutes 41 seconds; each selection took 32 ms to process, even in the opening minute._

_Per-student timing from the function logs, 24 Oct 2026. Rush = students who started within two minutes of their opening._

Half of all students made their first selection **within 10 seconds** of their window opening. A typical student in the 10:00 rush placed **four courses in 1 minute 23 seconds**, counted from the opening, not from when they arrived. CSE's regular students at 13:00 took longer: a median of **4 minutes 41 seconds** for four courses. That gap has nothing to do with the servers. The 13:00 wave was one large cohort going for the same popular sections. More of their taps came back "section full", so they picked a second choice.

_Chart: Share of students in the opening rush who had placed all their courses within a given time of their window opening: 10:00 rush 41.8% within one minute, 56.7% within two minutes, 72.0% within five minutes; 13:00 CSE rush 16.1% within one minute, 29.7% within two minutes, 51.9% within five minutes._

_Students in each opening rush who were done (last successful selection) within 1, 2 and 5 minutes of the opening. Section changes made more than 30 minutes later are not counted._

The server side of each of those taps didn't change at all under the rush. A successful selection took **32 ms** to process at the median and about **50 ms at the 99th percentile**, and the opening minutes at 10:00 and 13:00 were exactly as fast as a quiet afternoon. The waiting a student felt was the race for seats, not the platform.

That is the number this whole project was built to move: from five or six hours of refreshing to a few minutes, with half of the 10:00 rush done in under a minute and a half.

## Every refusal was a rule doing its job

Over the day, students sent **74,802 seat requests**, and **38,872 succeeded**. Every other answer was a rule working as designed:

_Chart: Why seat requests were refused on 24 October: the department's share of the section was full 21,882 times; the section itself was full 6,299 times; course evaluation not done 3,731 times; not eligible 1,243 times; already selected 116 times; two students on the same seat in the same millisecond 108 times; other 102 times._

_Refused seat requests by reason, 24 Oct 2026, from the function logs._

My favourite row is the sixth. **108 times**, two students went for the same seat in the same millisecond. The database granted it to exactly one of them, every time, which is the core promise in the [UCAM Cloud case study](https://monzim.com/work/ucam-cloud#decisions): a seat is granted by one guarded write. After the window, no section had more seats taken than it holds.

## Better every window

Put Fall 2026 next to the selection days before it, measured the same way each time, and the trend is the story:

_Chart: Server errors as a share of all requests on each selection day, on a log scale: Fall 2025 39.3%; Spring 2026 0.71%; Summer 2026 0.064%; Fall 2026 EEE wave 0.00065%; Fall 2026 full day 0.00011%._

_Server errors (5xx) as a share of all requests, whole selection day. Log scale: each step left is ten times fewer errors._

_Chart: p99 response time on each selection day: Fall 2025 about 5,100 ms; Spring 2026 136 ms; Summer 2026 115 ms; Fall 2026 114 ms._

_p99 latency for the whole selection day, from the API's metrics._

| Selection day | Requests (whole day) | Server errors (5xx) | p99 latency | Course seats placed |
| --- | --- | --- | --- | --- |
| Fall '25 · 8 Nov 2025 (earlier design) | 14.53M | 39.3% | ~5.1 s | not logged |
| Spring '26 · 23 Feb 2026 | 7.74M | 0.71% | 136 ms | 22,056 |
| Summer '26 · 4 Jul 2026 | 8.48M | 0.064% | 115 ms | 27,760 |
| Fall '26 EEE · 5 Oct 2026 (one department) | 1.08M | 0.00065% | 69 ms | 2,690 |
| **Fall '26 · 24 Oct 2026 (eleven departments)** | **7.00M** | **0.00011%** | **114 ms** | **29,047** |

A year ago, on the earlier design, four requests in ten failed and the slowest 1% took five seconds. This October, with more departments in one window than ever, it was 8 errors in 7 million and 114 ms. The seat count kept climbing too: **22,056** course seats in Spring, **27,760** in Summer, **29,047** in Fall, with none ever seen over capacity.

## Where the work actually went

The surprising number of the day wasn't the peak. It was how little of the traffic was selection itself.

_Chart: From requests to seats on 24 October, 09:00 to 20:00, on a log scale: 6.17 million API requests; 5.22 million backend calls; 74,802 seat requests; 38,872 successful selections; 29,047 distinct course seats placed._

_From requests to seats, 09:00–20:00, log scale. Seat requests were about 1.4% of the backend's work._

Almost 90% of the backend's work was the portal asking "anything new?" about things such as settings, banners, unread counts and eligibility. All of it was answered fast, and it's also the cheapest headroom we have. Fetching settings once per page load and pushing changes instead of polling will make the next opening lighter for the same number of students.

## The one thing that has to grow: the live seat board

In July I wrote about [the live seat board](https://monzim.com/blog/ucam-cloud-realtime-seat-board-websocket-redis), the screen that shows seat counts draining in real time. I built it under one rule: it is a spectator. If it ever falls behind, section selection must not notice.

On 24 October, it fell behind, and selection didn't notice.

_Chart: An illustration of the 13:00 hour: the top lane shows seat requests flowing steadily through a gate into seats with zero errors; the bottom lane shows the live seat board's hub running hot, its live connections fading, then all returning at once. A clock reads 12:59 live, 13:00 CPU 100 percent, 13:12 seat board quiet, 13:58 everyone back._

_Two lanes, one afternoon. Selection kept granting seats while the live view caught up._

When CSE's regular students came in at 13:00, thousands of students were watching live seat counts at once. The seat board's server ran at full CPU from 13:00 to about 13:58, and for most of that hour live counts and the "who's online" view stopped updating:

_Chart: From 12:50 to 14:10: users connected to the live seat board as a percentage of the day's peak, and the seat board server's CPU. Connections rise to 100% at 13:00 while CPU jumps to nearly 100%. Connections then fall to almost zero from 13:12 while CPU stays near 100% until about 13:57. At 13:59 a third of the peak reconnects at once and CPU drops to about 22%._

_Live seat-board connections (% of the day's peak, 30-second samples; missing samples drawn as zero) and the server's CPU (5-minute averages)._

Through that whole hour the selection API answered every request, with no server errors, and kept granting seats correctly. The live board is a convenience, not a dependency, so the worst case cost students a live animation, never a seat. The rule from July held on the day it mattered most.

The cause is simple arithmetic. Every seat change is pushed to every student watching that course, so the work grows with *connections × updates*. At 13:00 that was thousands of connections and, in the busiest 30 seconds, 4,693 seat updates, all on a small server that handles the rest of the year comfortably.

**The fix:** I'm rewriting the realtime server on **Erlang/Elixir**. [Erlang](https://www.erlang.org/faq/introduction.html) was designed for soft real-time systems with massive concurrency, and its virtual machine, the BEAM, runs huge numbers of long-lived connections, each doing a little work, on modest hardware. [Elixir](https://elixir-lang.org/) brings the same runtime with a friendlier toolchain, and [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html) is a proven pattern for exactly this kind of live, per-topic fan-out. Alongside the rewrite, the plan is to batch seat updates per course every few hundred milliseconds instead of sending them one by one, and to spread reconnects out so a slow moment can't turn into a stampede.

## What's next

- **The realtime rewrite** on Erlang/Elixir, load-tested well past this October's peak.
- **Less polling**: fetch settings once per page load and push changes instead.
- **Faster confirmation e-mails**: they ran up to 41 minutes behind at the peak because they were sent one at a time. Sending several at once brings that down to minutes.
- **Alarms for every number in this post**, so the next report writes itself while the window is still open.

On the second day, 25 October, the platform answered another 1.59 million requests by early evening, with a single server error.

Eleven departments, one afternoon, and a p99 that never left a fifth of a second. Students placed their courses in minutes, not hours, and went on with their Saturday, which is exactly what this platform is for.

## Methodology: how these numbers were measured

- **Requests, server errors and p99** come from the API's CloudWatch metrics for the whole Dhaka day, the same definitions as the [case study's results table](https://monzim.com/work/ucam-cloud#evidence). Charts that cover 09:00–20:00 say so.
- **Per-second rates and seat requests** come from all 22.8 million function log lines in the window, downloaded and parsed, not sampled.
- **Per-student times** start at the window's opening (10:00 or 13:00) and end at the student's last successful selection within 30 minutes of their first try. The "rush" is students whose first try came within two minutes of the opening. Processing time is the server's handling time for each successful selection.
- **Course seats placed** counts distinct student-and-course pairs with at least one successful selection between 09:00 and 20:00, so section switches count once.
- **Seat-board connections** come from 30-second samples of the live server, shown as a share of the day's peak; CPU is the server's 5-minute average.
- **Not published:** student, seat and enrolment totals (university policy), infrastructure identifiers, and how automation is detected.
