pauamma: Cartooney crab wearing hot pink and acid green facemask holding drink with straw (Default)
Res facta quae tamen fingi potuit ([personal profile] pauamma) wrote in [site community profile] dw_dev2009-09-26 09:15 pm
Entry tags:

Stats: Active accounts by payment level

One of the stats we need to provide is active accounts by account level (free, paid, premium, seed). Currently, the active accounts come from a per-cluster table with the user id as primary key and the time of last activity, in which users are added/updated whenever they do something that makes them active.

DW::StatData::ActiveAccounts handle collecting those stats, basically running something like the following request to retrieve the number of accounts active in the last 30 days:
SELECT COUNT(*) FROM clustertrack2 WHERE timeactive > UNIX_TIMESTAMP()-30*86400
That table doesn't include the account level, because (so far) it doesn't need to. This means that to break down active accounts by account level, I'd need to retrieve the complete list of active accounts and check the account level for each, which is much more costly than the current SQL.

To avoid that, the obvious solution is to add the account level to that table. However, this could cause discrepancies if the account level changes after the latest activity. For instance, if a paid account expires after the last activity, it would still be counted as "active paid" in the breakdown of active accounts, but free in the accounts by level stats, possibly leading to more accounts counted as "active paid" than as "paid". The only way to fix that is to have the account level transitions (paid account expiring, payment received, etc...) "correct" the table, but that gets rather ugly as the number of stats that needs a breakdown by account level goes up (and may not be possible for some stats at all, depending on how they're collected). Also, it can lead to biased data. For instance, if a free account gets paid for the first time after the last activity but before the stats are collected, it will be counted as paid even though all its activity was as a free account.

Adding the account level to the primary key (and thus, keeping separate activity records for each user id) isn't feasible, because that field would be null for existing activity records, which makes it unsuitable for a primary key. In addition, that wouldn't really help, because we'd still need to choose between activity records for the same account, lest we count it twice (or more). If having (according to stats) more active paid accounts than paid accounts (or than active accounts) makes stats look unreliable, think how much worse it would be to have more active paid accounts than total accounts. (Can you hear the "DW is inflating its stats" cries from here? :-) )

Anyway: the best compromise (IMO) seems to capture the account level in the activity table at the time that table is updated, and to leave it unchanged afterward. Opinions? Questions? Blindingly obvious solutions that I missed?

(Anonymous) 2009-09-27 02:59 am (UTC)(link)
Well, coming from complete ignorance of your volumes, and what "cost" means here, my typical brute force recommendation.

Build a memory table comprised of all (or all active) account numbers + status (presumably a single digit) and join to it when you want data by status. Hell, build it new every time you run stats. Can it be that costly?
ashcomp: (Default)

[personal profile] ashcomp 2009-09-27 02:59 am (UTC)(link)
Damn, did it again. That was me just now.
beki_d: (Default)

[personal profile] beki_d 2009-09-27 04:56 am (UTC)(link)
Depends on how you qualify active too. I don't post much (I haven't posted to LJ in months either) but I check my reading list daily.
jeshyr: Blessed are the broken. Harry Potter. (Default)

[personal profile] jeshyr 2009-09-27 08:56 am (UTC)(link)
I'm not sure if I'm missing something but what do you mean by "at the time that table is updated"? There's nothing I can see there that says there's a batch job for updating the table regularly or anything... sorry if I'm being exceptionally dumb here!

r
PS
Miss you!
shirokage: (Cloud)

[personal profile] shirokage 2009-09-27 11:14 am (UTC)(link)
Errr... I'm kind of new to SQL, but would it work if you added an Account Lv column (string), an Account Lv Start column (time), and Account Lv End column (time) to hold the account level, the time when the account level activated (eg. went from free to paid, or vice versa, etc.), and the time the level stopped being valid (eg. paid account expired, etc.)?

This way you can keep track of when the account level was changed and when the last activity was, thus perhaps avoiding biased data? Because this way, you can select the active accounts with paid status that have the their last activity while in the paid period.

SELECT  COUNT(*)
FROM     clustertrack2
WHERE  AccountLvl = 'Paid' AND
             TimeActive > AccountLvStart AND
             TimeActive < AccountLvEnd

(This code should select the paid users that used their accounts within their paid account period?)

But this relies on the updating the table every single time the status of the account is changed (account getting paid or expiring) and it will generate a lot of SQL entries.

I hope this helps, but, yeah I'm still not very good with SQL. If this was complete silly, then I apologize. *hides*