It was arguably the most watched election in decades, and thanks to Microsoft Azure, the Portland Press Herald, Kennebec Journal and Morning Sentinel newspapers had Maine’s only real-time results for local and statewide races on Election Night 2016.
Best of all: It cost MaineToday Media, the parent company of those newspapers, $15.58 to provide them.
Yes, you read that correctly: Fifteen dollars and fifty-eight cents, to gather and deliver 50 GB and 1.3 million requests of real-time election results JSON over 24 hours.
MaineToday’s competitors either didn’t bother creating real-time live local results, or their live results failed early and often. But Azure’s rock-solid reliability and massive scale ensured those results were available for 1.3 million requests over 24 hours.
It was an impressive performance, especially considering how little it cost, and here’s how I built it.
Little is more important to a newspaper than an election and nothing is more important than a presidential election, especially one that turned out to be arguably the biggest shock since Truman defeated Dewey.
Ensuring accurate and immediate results was central to the newspapers’ coverage of the election. Understanding that, my requirements for this solution were, in order:
- It must serve results without error. The JSON files I was creating had to respond to every request, without error, and as quickly as possible.
- It must load quickly and be as lightweight as possible. We anticipated that most election results traffic, especially during peak demand on Election Night, would be viewed on phones or tablets. Therefore, the data files had to be as small as possible, both in terms of records contained and physical size; the less demand on the client to retrieve and parse the file, the better.
- It must refresh results as fast as possible. The target refresh time was 3 minutes.
- It must be flexible enough to allow for on-the-fly editing of local races and real-time reporting of race statuses.
- We not only wanted to ensure local races could be updated immediately, but that corrections such as name misspellings could be made at the same time; and that write-in candidates could be added for races without official candidates.
- MaineToday’s reporters, as well as its Associated Press data collection partners, needed to know which Maine towns had reported results, and be able to see results for all towns.
The tools I ended up using were:
- Three Azure Functions, all running on timers.
- Two of these functions retrieved XML from the Associated Press, which held national and state presidential results. They turned those files into JSON and saved them as blobs in an Azure Storage account.
- A third function built a special JSON file that contained results from six select races. This JSON was used to display those races on the newspapers’ website home pages.
- A Windows A2 Standard virtual machine. This machine ran an FTP server that accepted text files from the AP. It parsed those files as they arrived, converting them into several JSON files, which again were sent to Azure Storage.
- Two Azure Storage accounts. One provided storage for the Azure Functions. The other served the JSON election results files.
- I could have created a single Storage account to handle both needs; however, I wanted to use an existing Azure Storage account to host the JSON, and it’s located in East US. Azure Functions weren’t available in East US, so when I spun up the functions in East US 2, Azure made a storage account there.
- A Standard S2 Web App. This app hosted an ASP.NET MVC website that was the GUI for saving local results and seeing the status of statewide race results from the AP.
- A Standard S2 Azure SQL Database. This database held the local results collected through the Web App.
- I probably could have gotten away with using smaller instances of both the Web App and Azure SQL Database, but I wanted to make sure they would perform quickly against multiple simultaneous requests.
The solution created 11 JSON files. Ten of them were used to build the public-facing results:
|File purpose||File size||Records|
|Maine presidential results by district||2.3 KB||3|
|National presidential results||1 KB||5|
|Select county races||1.5 KB||4|
|Maine House of Representatives||46.5 KB||151|
|Maine Senate||11.1 KB||35|
|Statewide referenda results||2 KB||6|
|Statewide US House results||1 KB||2|
|Homepage races||2.1 KB||6|
|Local results for pressherald.com||27.6 KB||74|
|Local results for centralmaine.com||17.1 KB||43|
Additionally, I created a master data file, for internal use by reporters and the AP data collection team, for tracking town-by-town election results.
This file weighed in at a hefty 1.7 MB and contains 5,329 records. I deemed that acceptable because the file would see light traffic and would be parsed by relatively late-model desktop computers using current browsers.
The front end
Fortunately, the editorial department of the Press Herald wanted to have results grouped by office; i.e., president, Congress, Maine House, etc.
This gave us a natural partitioning of the data that made sense. It also provided us with a means of solving our biggest concern: Keeping things lightweight and fast on mobile.
By splitting each tab of the results page into its own data file, we could load and bind that data only when the tab was selected — significantly improving performance.
This also let us significantly reduce load on MaineToday’s web servers, helping them stand up to the load they saw overnight and into the next day.
Offloading election results binding to clients meant the websites wouldn’t be stressed either serving the actual results or processing them.
In other words, we would keep the data files small so they could be easily loaded and databound by client-side scripts. And we would serve them from a different source than the website servers.
This allowed us to deeply cache the election results pages themselves, and to avoid stressing the web servers, which were already under high demand for constantly updated news stories and homepages.
Performance was amazing, throughout the 24 hours of peak Election 2016 viewing.
The charts below show the home page and election results page views for pressherald.com and centralmaine.com from 6 p.m. EDT on Election Day through 6 p.m. EDT on 9 November 2016.
Overall, we served 1.3 million JSON file requests for those files, directly from a Classic Azure Storage account, over that 24-hour period.
At peak demand, around 9 p.m. EDT on Election Night, we served about 52,000 requests — or about 14 requests per second — without error.
As the images below show, most of these requests were, as we expected, from phones and tablets.
There were a few glitches along the way, and I’ll describe those in upcoming blog posts getting into the particulars of this solution.
But on the whole, I couldn’t be more pleased with the results.
Results were processed without error. To the best of my knowledge, there were no errors serving JSON files to clients, even at peak demand. (Admittedly, I don’t have those metrics; but I also don’t have any complaints and believe me, if there was a problem with requesting results, I would have heard about it.)
There will be continuing costs for serving the JSON files in this solution. However, my expectation is that cost will be about $10 per year.