Case Study: SFI After School Club
5 August, 2021
School Fitness Ireland is a truly inspirational company led by Martin Sheehan, who is one of the most genuinely caring, creative and fun clients I’ve had the pleasure of working with in recent memory.
After sitting in on meetings with him and his team, I always leave pumped – thinking “alright, what else can do with this project to make it even bigger and better”. Their passion and excitement for the wellbeing of children and their health is honestly more contagious than covid.
With a huge team of world class coaches who offer fitness classes to schools in the whole of Ireland (yes, including the Aran islands), they have the goal of “providing the highest quality of affordable, non-competitive exercise to children nationwide… encouraging children to exercise and play with family members outside of our class time.“
I’m sure everyone is bored of covid-19 references when it comes to case studies explaining how a business “pivoted to the new digital-first landscape” so this next paragraph is the one and only time I’ll mention it.
With the uncertainty of school lockdowns, they didn’t miss a beat when it came to augmenting the business from taking bookings for physical classes online, to actually delivering their classes online too.
The project is called School Fitness Ireland’s After School Club.
The project scope
403Page Labs had already built the School Fitness Ireland corporate site on WordPress. We wanted to keep the membership/class-delivery site on a separate stack (but also WordPress) so we could tailor the hardware and codebase for scalability without having to consider impacting the existing corporate site – an unnecessary limitation.
The requirements were as follows:
- Ability to scale to hundreds of thousands of logged-in users without performance degradation or timeouts
- Allow schools and organisations to buy bulk memberships for their students/staff
- Allow parents to create accounts for their children
- Securely deliver video classes from the coaches every weekday on a schedule
- Regular secure backups, every 6 hours
- Restrict downloading of videos for unauthorised redistribution
- Cookie/GDPR compliance
The MVP was delivered within 2 weeks.
How did we do it?
The hardware stack itself is something 403Page Labs has been cooking up for some time. I won’t go into much detail here cause that’s our super secret formula, but I will share that it’s an Ubuntu 20 system, optimised for WordPress on nginx, php-fpm, redis, OPcache and runs Cavalcade for cron management (which is key for reliably expiring/renewing memberships and publishing classes on their scheduled dates/times).
Among other security features, the server is configured to only allow requests from Cloudflare IPs, ensuring that traffic is vetted through their enterprise-grade security layer, while also benefiting from their more than 155 edge locations for delivering static assets – with the exception of video.
The video content is delivered from a private AWS S3 bucket in the Ireland data centre for minimal latency & cost while reducing bandwidth on the site server for best performance. We also use this bucket for the backup cron job that runs every 6 hours for the database and every 24 hours for the file system.
We decided to use Memberpress to handle memberships, access control and payments with Stripe. It was a toss up between that and Learndash – which I love – but after consulting with WordPress design-guru Jessica Devine (which I often do ahead of new projects), she brought to my attention that the native AWS S3 support for video content and access control in Memberpress would drastically reduce development time.
Some creativity was needed when it came to content management. As mentioned, Memberpress has an add-on that allows you to pair up with your AWS S3 bucket which is great.
What’s not so great is every time you want to upload a video to a post in WordPress, you need to:
- leave WordPress’s admin
- login to the AWS console
- upload the video
- copy the path to the video object from S3 and;
- write a shortcode in a WordPress containing the path in a post block to display it
This. Is. Painful. Especially for the admins of a site that published 5-6 videos each week.
Here’s how we solved it
We wiped out the first major hurdle with the help of the folks at Delicious Brains. Their lengthily-titled WP Offload Media Lite for Amazon S3, DigitalOcean Spaces, and Google Cloud Storage plugin was ideal for allowing admins to upload the video files directly through WordPress’s media library, which are then automatically sent to the S3 bucket (and removed from local storage).
This introduced a new problem. Because the S3 bucket is private, any other non-video static files (like
.webp files) would be locked down on the same bucket as the videos – so any newly added images won’t load on the site because those requests don’t include the public/private keys for the bucket (meaning they’ll simply 403). We ONLY wanted video to be offloaded to S3 – not all statics.
So we made a plugin that interrupts the plugin’s offload function if the file extension didn’t match
.mp4. You can see how we did that here.
We were still left with the problem of having to login to the AWS S3 console, copy the video object path and paste that in a shortcode for each class.
We solved this one by creating a our own Gutenberg block that allows an admin to select the video file from the WordPress media library (with validation that only allows
.mp4 files), rewrites the path to how Memberpress likes it and embeds the path in a shortcode without anyone having to leave the page.
I considered this not just a usability win, but an ethical one, as no WordPress site administrator should need to be subjected to the infinite complexity of the AWS dashboard. I named the block S3 Video because I’m super creative.
We also hooked into WordPress core’s upload function and changed the upload process to run in asynchronous “chunks” to alleviate large upload size pressure.
Every WordPress developer knows 2 things; caching WordPress resources are essential for scaling and; membership sites are really difficult to cache.
Each logged in user needs to be able to see their version of a page as the site needs to be able to validate that their login credentials are legitimate and they have access to the content they’ve paid for.
We also need to make sure that a logged-out user isn’t able to see a cached version of a page from a logged-in user, which would give them the content for free by refreshing the page until they got lucky.
With that said, we were still able to cache a whole lot of the site. The PHP code WordPress executes is cached by OPcache, and database queries are cached by redis. FastCGI caching is also configured within the nginx layer and Cloudflare adds a further layer of page/static caching at their edge. Absolutely everything that can be cached is cached – as the good lord intended.
This is part of how we ensure that when the server needs to scale – it really needs scale and we’re not just throwing hardware at an unoptimised build.
It’s worth noting that all of these caches can be cleared from the admin area, no need for the team to have to learn linux command line to troubleshoot a stuck static or CSS change…
A site like this needs a reliable email system. We hooked MailGun’s API to all of the site’s sendmail functions to ensure deliverability and insightful logging – we’re not a huge fan of stock PHPmailer.
We leveraged TrimPress from our friend (and awesome WordPress developer) David Matthew as we do with almost every WordPress build we make to strip out a bunch of unused nonsense like oEmbed and XML-RPC and reduce Heartbeat ajax call frequency in a couple of clicks.
We configured activity logging that tracks all site events and changes – which is essential for troubleshooting and security auditing. There is an uptime monitor in place that alerts if the server runs into problems (memory exhaustion, CPU overload, etc.).
In the event of a catastrophic data centre failure (think power failure, meteorites or earthquakes where the DC is), we can recover the site in less than 30 minutes to a redundant location in a completely different country.
Stock wp-cron.php is absolute garbage at scale so we use a unix cron to ensure that Cavalcade (our job manager) is always running on the box. This processes much of the grunt-work, like ensuring classes are published on time and processing regular snapshots of the file system and database – which is then zipped up and shipped to our S3 bucket each day (4 times daily for the database).
The machine itself can be scaled by many orders of magnitude on-demand to control costs off-peak and scale up when traffic is booming.
Copy protection is in place that prevents users from downloading local copies of the on-site video content.
The site is GDPR compliant and user data is encrypted-at-rest.
There is also a Slack integration for customer support which pipes queries from visitors directly to the organisations workspace, ensuring problem resolution time is kept swift and easy.
There’s more to come!
We have loads more coming on this project, it’s on a significantly accelerated growth trajectory and we’re all super excited to see it blow up. I’ll publish a follow up when it makes the most sense.