Failure isolation and cache
Why it is needed
Section titled “Why it is needed”School servers can be slow (the extracurricular schedule sometimes takes tens of seconds), can stop answering for a while, or can change the shape of a response. If the whole app stopped whenever one place failed, that would be a problem. And sending the same request to the school once per user would put load on its servers.
The flow of one call
Section titled “The flow of one call”Failure isolation of public data
Section titled “Failure isolation of public data”Every method of the public data client (SejongPublicData) follows the same rules.
- A connection failure, timeout, non-2xx status, non-JSON body, missing expected key or a school
_SUBMIT_ERROR_answer all return without an exception: the last cached value (even if expired) or an empty value (an empty list, an empty object for dormitory notices). - Internal school server details (such as
STACK_TRACE) are never logged or returned. - Only invalid input throws:
curriculum_courseswith a blank department code or course number, andgraduate_calendarwith a malformed site id (INVALID_INPUT, no request).lecturesnever throws for its filters. - A missing value is an empty string (
0forviewsandtotal,0.0for coordinates), and one odd row never fails the whole list. Only a missing or non-array container (data,items,eventsand so on) counts as a failure.
| Item | Default | Behavior |
|---|---|---|
| Cache lifetime | 86400 s (24 hours) | Stored in memory per method and arguments. A fresh entry means no request |
| Failure memory | 60 s | After a failure with no cached value, the same call returns the empty value without a request for this long. A stale cached value is still returned on failure |
| Maximum entries | 500 | When full, the oldest entry is evicted |
- Concurrent calls with the same arguments share one request.
- Locks are per key but released together with their entries, so even user-typed search text does not make memory grow without bound.
- One call is one request as a rule. The exception is
cafeteria_places: it requests ids 1 to 10 one after the other, up to 10 requests, and one failed request fails the whole list so that a partial list is never cached for a day. - A whole undergraduate term of lectures is a few MB, so one cache entry is large. Prefer one department at a time (Course listings).
- Create one
SejongPublicDataper process. The cache and the request merging live inside the instance.
The settings (cache_ttl_seconds, failure_cache_seconds, cache_max_entries) are in Common rules.
Failure isolation in authentication
Section titled “Failure isolation in authentication”| Step | If it fails |
|---|---|
| Input check, portal login | The whole call fails |
| DHC (student info, classic reading) | The whole call fails |
| SIS (contact, school codes) | The call succeeds, only those fields are null |
Portal main (parentUnitName) |
The call succeeds, only that field is null |
Authentication and public data never affect each other’s failures. A dependency rule test keeps the two code bases from importing each other.
If you implement it yourself
Section titled “If you implement it yourself”- Do not let an external server failure become an app failure, and keep the last good value.
- Remember a failure briefly instead of repeating the same request right away.
- Merge concurrent requests.
- Source: Failure isolation and cache in spec/PUBLIC_DATA.md
- Daily live portal check
