@giles Thanks for your analysis.
I doubt the problem is db deadlocks. (In fact, only today did I separate the tasks into the two hourly tasks to aid debugging. Previously it all ran linearly off of one shell script, and exhibited the same behavior.)
Thanks for your offer to check the scripts. Let me explain briefly:
(1) refresh_cache.sh runs hourly and logs to /tmp/refresh_cache.log. (It actually contains some logic to not do anything between 01:00am and 07:00am my local time, nor on weekends.)
(1.1) refresh_cache.sh runs a web2py script called refresh_cache.py which logs to /tmp/seaceperu.log. It does some screen scraping and upserts to MySQL.
(1.2) Then, refresh_cache.sh runs a web2py script called send_change_alerts.py, which queries the database for new content, alerts the users via email, and updates a few records in MySQL.
Step (1) as a whole takes under a minute, sometimes up to two.
(2) The web2py script new_prospects.py also runs hourly. It logs to /tmp/new_prospects.log. (It should be running off of refresh_cache.sh, but I took it out for debugging purposes.) This script queries the database, may send some emails, and may do some MySQL updates. It seems to stall at different places each time, which appears to rule out some problem with the queries or the data. This script also normally completes within a minute.
Steps (1) and (2) always work fine when run off the command line. But when run from the Schedule tab, the jobs can get stuck at different places; if left unattended, they do indeed start piling up.
(3) There is a daily web2py script called send_nearby_milestones.py which queries the database and sends some emails. I haven't yet seen it give any trouble.
Please feel free to review the code or check the mysql logs. I'll keep debugging to see if it's a problem in the code, but right now it seems to me that something is amiss with the MySQL connection. I doubt deadlocks are the issue because this is all linear. Thanks for your assistance!