mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-12 16:15:43 +00:00
A guess is credited to the district its panorama sits in, then to that district's province, then to Vietnam. Each level keeps its own board, so a player can top District 7 without touching the national table. Every existing point survives untouched. The country maps to the leaderboard:vietnam and distance:vietnam keys that already exist rather than to a new leaderboard:city:vn, so the national board keeps accumulating instead of restarting. The ':city:' segment in the key namespace is now a misnomer -- it holds district and province codes alike -- but renaming it to ':region:' would strand every key holding a player's history, so it stays, with a comment recording why. Da Lat and Duc Hoa keep their bare codes for the same reason. Ha Noi, Da Nang and Ho Chi Minh keep their totals on the province: those points predate districts and cannot be attributed to one, so their boards stay continuous while district boards start at zero. Levels are written in parallel. They are independent keys and none reads another's state, so serialising them would add two round trips of latency to every guess for nothing. Region codes and the leaderboard limit are validated inside the library rather than only in the routes. The key builder lowercases whatever it is handed straight into a Redis key, and the routes are not its only callers -- the migration script and anything added later bypass them entirely. The adapter gains scanKeys, which the migration needs to enumerate what it is about to touch. It applies the key prefix to the pattern and strips it from the results, because a caller that scanned 'leaderboard:*' directly would match nothing at all: every physical key carries the prefix, and an empty result is indistinguishable from an empty database. An empty KEY_PREFIX is no longer accepted, since enumeration with no prefix would reach every other project sharing the database. The migration seeds Lam Dong and Long An from Da Lat and Duc Hoa, whose history they inherit. It runs after the deploy, not before: migrating first leaves a window where a Da Lat guess credits the town and the country but not the province. It copies absolute scores so a second run converges rather than doubling, and empties the destination first so a player trimmed out of the source cannot survive in the copy with a stale score. An empty source is refused before that delete -- otherwise the destination is wiped and nothing written back, which reads as a clean no-op. Afterwards it confirms each destination matches its source and that nothing else went backwards, accepting that boards grow while the app serves traffic. Its backup can be restored through the same script. The migration logic lives in scripts/lib/ so its guards are reachable from a test rather than only by running it against a live database.