Diff -- /var/www/vhosts/webstls.com/httpdocs/_storage_alert_worker.pl
Diff

/var/www/vhosts/webstls.com/httpdocs/_storage_alert_worker.pl

added on WebSTLs (webstls.com) at 2026-07-01 22:27:11

Added
+118
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 0c5bad5db674
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1#!/usr/bin/perl
2#======================================================================
3# WebSTLs -- hourly storage worker.
4#
5# Three jobs per pass:
6# 1. Soft-alert sweep -- find users at or above
7# platform_settings.storage_soft_alert_pct (default 80) whose
8# last_storage_alert_at is NULL or > 7 days ago. Stamps
9# last_storage_alert_at NOW() and writes a per-user note row in
10# email_sends_log so the email-blast worker (if present) can
11# pick them up.
12#
13# 2. Drift recompute -- for users whose storage_used_at_recompute
14# is NULL or > 24 hours ago, run Storage::recompute_usage to
15# reconcile the denormalized counter against the authoritative
16# model_files sum.
17#
18# 3. Stats line at the end summarizes counts for the cron log.
19#
20# Cron suggestion (server crontab for the vhost user):
21# 17 * * * * /usr/bin/perl \
22# /var/www/vhosts/webstls.com/httpdocs/_storage_alert_worker.pl \
23# >> /var/www/vhosts/webstls.com/httpdocs/_storage_alert_worker.log 2>&1
24#======================================================================
25use strict;
26use warnings;
27use lib '/var/www/vhosts/webstls.com/httpdocs';
28
29use MODS::DBConnect;
30use MODS::WebSTLs::Config;
31use MODS::WebSTLs::Storage;
32use MODS::WebSTLs::Mailer;
33
34my $RECOMPUTE_AGE_HOURS = 24;
35my $ALERT_REPEAT_DAYS = 7;
36
37my $cfg = MODS::WebSTLs::Config->new;
38my $db = MODS::DBConnect->new;
39my $DB = $cfg->settings('database_name');
40my $dbh = $db->db_connect();
41die "could not connect to $DB\n" unless $dbh;
42
43my $started = scalar localtime;
44my $storage = MODS::WebSTLs::Storage->new;
45
46# ---- 1) Drift recompute ----------------------------------------------
47# Pick every user who hasn't been recomputed in 24 hours. Each call to
48# recompute_usage runs one COUNT/SUM on model_files so the workload is
49# bounded -- even at 10k users that's well under 5 minutes / pass.
50my @stale = $db->db_readwrite_multiple($dbh, qq~
51 SELECT id FROM `${DB}`.users
52 WHERE storage_used_at_recompute IS NULL
53 OR storage_used_at_recompute < DATE_SUB(NOW(), INTERVAL $RECOMPUTE_AGE_HOURS HOUR)
54 ORDER BY storage_used_at_recompute IS NULL DESC, id ASC
55 LIMIT 5000
56~, $0, __LINE__);
57
58my $recomputed = 0;
59foreach my $u (@stale) {
60 $storage->recompute_usage($db, $dbh, $DB, $u->{id});
61 $recomputed++;
62}
63
64# ---- 2) Soft-alert sweep ---------------------------------------------
65my $needs_alert = $storage->users_needing_soft_alert($db, $dbh, $DB);
66my $alerted = 0;
67my $send_failed = 0;
68my $mailer = MODS::WebSTLs::Mailer->new;
69
70# Pre-compute next-tier upgrade label per tier slug so vars come out
71# right per recipient.
72my %upgrade = (free => 'Starter', starter => 'Pro', pro => 'Studio', studio => 'Studio');
73
74foreach my $row (@$needs_alert) {
75 my $uid = $row->{user_id};
76 my $pct = $row->{pct};
77 my $used_b = $row->{used_bytes};
78 my $used_h = MODS::WebSTLs::Storage::human_size($used_b);
79
80 # Pull cap + tier for the template vars.
81 my $L = $storage->limits_for_user($db, $dbh, $DB, $uid);
82 my $cap_h = MODS::WebSTLs::Storage::human_size($L->{hard_cap_bytes});
83 my $upgrade_to = $upgrade{ $L->{tier} } || 'Studio';
84
85 # Send the email via Mailer::send_template -- pulls the
86 # storage_soft_alert row from email_templates, fills vars,
87 # appends the unsubscribe footer, respects user preferences,
88 # logs to email_sends_log. Returns 1 on send, 0 on suppressed/fail.
89 my $ok = eval { $mailer->send_template(
90 slug => 'storage_soft_alert',
91 user_id => $uid,
92 vars => {
93 pct => $pct,
94 used => $used_h,
95 cap => $cap_h,
96 upgrade_to => $upgrade_to,
97 },
98 ) };
99 if ($ok) {
100 # Only stamp last_storage_alert_at when we actually SENT (or
101 # the template suppressed it via prefs -- either way we won't
102 # re-process). Failures stay flagged for the next pass.
103 $db->db_readwrite($dbh, qq~
104 UPDATE `${DB}`.users
105 SET last_storage_alert_at = NOW()
106 WHERE id='$uid'
107 ~, $0, __LINE__);
108 $alerted++;
109 } else {
110 warn "[storage worker] send_template failed for uid=$uid\n";
111 $send_failed++;
112 }
113}
114
115$db->db_disconnect($dbh);
116
117print "[$started] storage worker: recomputed=$recomputed soft_alerts_sent=$alerted send_failed=$send_failed\n";
118exit 0;
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help