CRM: Trial prospects worker#6498
Conversation
| score = | ||
| TrialProspects.score( | ||
| traffic.estimated_monthly, | ||
| Teams.Billing.features_usage(team, site_ids), |
There was a problem hiding this comment.
This one line starts 10 postgres queries, so I'm a bit worried about postgres stress at the time of this worker: the more teams are created, the more stress there'll be.
There was a problem hiding this comment.
Correction, the more teams there are to score, the longer the period of stress will be: the intensity will remain constant due to sequential approach.
There was a problem hiding this comment.
IMO sequential is better because it means the job runs longer but it will never occupy more than 1 database connection. Parallel processing would be faster but also create more db pressure while it's running.
Not too worried about occupying a single db connection at 4am UTC, it would be a problem if the job runs too long.
There was a problem hiding this comment.
If the job turns out to be too slow I can look into reducing the amount of postgres roundtrips in Billing.feature_usage
| def perform(_job) do | ||
| rows = | ||
| Date.utc_today() | ||
| |> trial_population() | ||
| |> Enum.flat_map(&score_team/1) | ||
|
|
||
| persist(rows) | ||
| :ok | ||
| end |
There was a problem hiding this comment.
If any of the numerous postgres queries run as part of the job fails, the effort spent on the whole batch is wasted. Could we create separate jobs to score and insert / replace each team?
There was a problem hiding this comment.
Good point overall but feel like separating the jobs is a bit overkill. This functionality is not critical at all and failures have a low cost.
What do you think about limiting the blast radius so that in case something goes wrong during scoring we skip that team, log it to Sentry, and continue with the batch?
There was a problem hiding this comment.
Yes, that's a reasonable alternative. Maybe a little retry with delay for each as well?
apata
left a comment
There was a problem hiding this comment.
We discussed implementing this on a separate BI platform, but just to try something out first, I think it's worth it.
zoldar
left a comment
There was a problem hiding this comment.
Left some optional suggestions
| site_k = site_count_kind(site_count) | ||
| member_k = member_count_kind(member_count) | ||
|
|
||
| kind = feature_k |> higher_kind(site_k) |> higher_kind(member_k) |
There was a problem hiding this comment.
| kind = feature_k |> higher_kind(site_k) |> higher_kind(member_k) | |
| kind = Enum.max_by([feature_k, site_k, member_k], &@kind_rank[&1]) |
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
Changes
Context: Marko has wanted the CRM to be able to surface trial prospects better so we can support teams with high potential better during the trial.
This PR adds a postgres table and a background worker to score trial prospects by potential MRR based on actual usage. UI will be added in a follow-up PR. The UI will support ranking trialing teams by MRR, traffic, and signup date to keep an eye on active trials along with their potential if they choose to upgrade.
The background worker scores the whole trial population (= teams with no subscription and up to 30 days after trial expiry) every night. On each run previous scores are overwritten and stale entries removed from the table.
Migration will be extracted once PR is approved.
Tests
Changelog
Documentation
Dark mode