XenForo Experience System

XenForo Experience System

I built an experience system for the next version of Hive. That might be news to you if you frequent the site. I won't go into anymore details about it as that's for a news post on HIVE in the next few months.

The problem is that I want to give experience points for various actions on the site. I want to do so easily, meaning, I don't want to do too much paperwork.

See, I started down a wrong path of hooking into XF's code every time (e.g.) a post was created, deleted, bulk deleted, hard-deleted, etc. There are many places to hook into and if I miss one of them, there will be a "leak" so-to-speak of experience that I don't want dangling. I don't want a member to find a way to post, delete and re-post or something to boost their experience and causing a bug leading me to re-calculate their exp. This path lead to a LOT of hooking in to XF and it was not elegant. It's one of those solutions that suddenly causes you to pretend you have less ideas than you have because you know the work is too hard or error prone to get done.

I was syncing a "experience" table in the database with the actions a user made on the site. I was marking exp as "counts", "does not count" "counts for X exp" depending on the forum a post resided in and whether or not it belonged to a thread under moderation, etc. It was just the wrong solution.

I started liking the idea of calculating the experience of a member from scratch each time their interacted with the site in a meaningful way (e.g. posting sometihng) more and more. So I reduced my number of hooks into XF by 2/3 and replaced all the hooks with a call to scheduleRecalculateExperience with the member as argument.

This was slow, so I made it so a user's experience is only recalculated every 10 minutes at worst. Keep in mind, this is only for meaningful actions, so it's not like every member (in my case 90,000+) would get their experience recalculated every 10 minutes, but maybe more like 50 people or less.

It was still slow, but I had many performance tweaks to make. For example, I was joining ALL meaningful tables using MySQL UNION ALL queries to make for a new newsfeed sort of view. This query made it very easy to make pagination, etc, but UNION ALL is slow. Instead of doing any sort of union, I just did the queries separately and that sped it up 10-20x to a place where the members who've been on the site the longest would take only a few seconds to recalculate.

This was a success. Experience gets fully recalculated based on the data and if the data ever changes in a way where I don't have a hook, the experience will eventually be consistent when another hook gets triggered.

This system is super easy to extend to new data types and I can be as sloppy as I want with my hooks as long as the most used actions such as replying to a thread is covered.