The job fair was in a university gymnasium, in February, no heating on. Two of us arrived at 7 AM with laptops and one coat each. The event site had launched a week early: booth lookup, resume submission, on-site QR check-in. What really kept us on edge was the first two hours after doors opened.
Where the QR Codes Went
The client originally wanted one QR code in the middle of a poster — scan, land on the homepage, then tap 'Check in.' We pushed back: each table card got its own QR code with the booth number in the URL, landing directly on the check-in page. Two fewer taps. The codes were batch-generated by script — 87 booths, 87 images, named by booth number, printed and triple-checked.
The check-in page does exactly one thing: enter the last 4 digits of your phone number, submit. No name, no school, no registration. The venue network is the gymnasium's public Wi-Fi — every extra field strands another batch of people at the keyboard.
9:10, the Logs Start Climbing
Doors at 9:00. First requests at 9:08; the peak ran 9:10 to 10:40. The two of us watched Nginx access logs with tail -f and awk, counting POSTs per minute. The peak was about 140+ submissions a minute — not huge, but all funneled into one PHP endpoint.
Just after 10, a few people said 'I tapped and nothing happened.' The logs showed 200s and 500s mixed; the 500s were database connection timeouts. That MySQL had max_connections set to 100, PHP-FPM was running 32 workers, and the resume page was querying too — connections maxed out. With no way to restart services on site, we temporarily disabled the resume page's 'real-time duplicate check,' which hit the database on every keystroke of a phone number — pure waste. Connections dropped immediately.
In the retrospective: that real-time check had been added casually during development, and nobody flagged it.
Those 87 QR Codes Got Reprinted Once
The first batch of table cards came back with 3 booth numbers mismatched — a table row had slipped during layout. We caught it during the day-before check; the print shop reprinted overnight, and the glue was still wet when we stuck them on. Since then, the QR generation script exports a cross-check table — file name, booth number, URL, three columns — printed and manually ticked off before anything goes to print.
And an unexpected bonus. The day after the fair, the client asked: can we see which time slot was busiest? The check-in table only had timestamps, so we GROUP BY'd 10-minute buckets and exported a 12-row table. The peak fell between 9:40 and 9:50. They used it to plan the next event's floor layout, moving popular employers near the entrance. That requirement was never on the original list — it surfaced on its own once the data was sitting there.
The Public Wi-Fi Problem
The venue network belongs to the gymnasium; we had no control over it. After getting burned the first year, we now bring our own 4G router as backup to events like this, and the check-in page saves records to localStorage when offline, syncing once a connection returns. The 2017 event had none of that — we got through by killing the duplicate check, which looks dicey in hindsight.
On the subway home, a message arrived: 'Didn't crash.' Two words; none of us replied. Half an hour later someone sent a red envelope captioned 'all 87 stuck on straight.' That 'on-site kill switch' list has been used since — printed before every event and kept under the glass of the check-in desk. By year three it had one more line: plug in the backup router first. The cross-check table is still around too — 87 rows, with a handwritten 'reprint 3' on the last line. Every later event edits a copy of it, saves a year-stamped version, and the original stays untouched.
How the Data Was Collected
Check-in records went into one table with four fields: id, last 4 digits of phone, booth number, timestamp. That afternoon we exported a CSV — 2,000+ rows — for the client to match against their resume database. They built an Excel pivot by booth number: how many check-ins each booth collected, which booths sat empty — all obvious at a glance.
As the fair wound down and the gymnasium emptied, we closed our laptops, coats barely worn. On the subway home the client messaged: 'Didn't crash.' Just two words, and they felt good.
For events like this we now carry a fixed 'on-site kill switch' list: real-time duplicate checks, SMS notifications, heavy reports — all off during peak hours.

