There were 11 main types of changes:
- UPDATE's and DELETE's sometimes had LIMIT 1 at the end of them. This is not only non-compliant but
it would certainly not do what whoever wrote it thought it would. It is likely this mistake was just
copied from Friendica. All of these instances, the LIMIT 1 was simply removed.
- Bitwise operations (and even some non-zero int checks) erroneously rely on MySQL implicit
integer-boolean conversion in the WHERE clauses. This is non-compliant (and bad programming practice
to boot). Proper explicit boolean conversions were added. New queries should use proper conventions.
- MySQL has a different operator for bitwise XOR than postgres. Rather than add yet another dba_
func, I converted them to "& ~" ("AND NOT") when turning off, and "|" ("OR") when turning on. There
were no true toggles (XOR). New queries should refrain from using XOR when not necessary.
- There are several fields which the schema has marked as NOT NULL, but the inserts don't specify
them. The reason this works is because mysql totally ignores the constraint and adds an empty text
default automatically. Again, non-compliant, obviously. In these cases a default of empty text was
added.
- Several statements rely on a non-standard MySQL feature
(http://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html). These queries can all be rewritten
to be standards compliant. Interestingly enough, the newly rewritten standards compliant queries run
a zillion times faster, even on MySQL.
- A couple of function/operator name translations were needed (RAND/RANDOM, GROUP_CONCAT/STRING_AGG,
UTC_NOW, REGEXP/~, ^/#) -- assist functions added in the dba_
- INTERVALs: postgres requires quotes around the value, mysql requires that there are not quotes
around the value -- assist functions added in the dba_
- NULL_DATE's -- Postgres does not allow the invalid date '0000-00-00 00:00:00' (there is no such
thing as year 0 or month 0 or day 0). We use '0001-01-01 00:00:00' for postgres. Conversions are
handled in Zot/item packets automagically by quoting all dates with dbescdate().
- char(##) specifications in the schema creates fields with blank spaces that aren't trimmed in the
code. MySQL apparently treats char(##) as varchar(##), again, non-compliant. Since postgres works
better with text fields anyway, this ball of bugs was simply side-stepped by using 'text' datatype
for all text fields in the postgres schema. varchar was used in a couple of places where it actually
seemed appropriate (size constraint), but without rigorously vetting that all of the PHP code
actually validates data, new bugs might come out from under the rug.
- postgres doesn't store nul bytes and a few other non-printables in text fields, even when quoted.
bytea fields were used when storing binary data (photo.data, attach.data). A new dbescbin() function
was added to handle this transparently.
- postgres does not support LIMIT #,# syntax. All databases support LIMIT # OFFSET # syntax.
Statements were updated to be standard.
These changes require corresponding changes in the coding standards. Please review those before
adding any code going forward.
Still on my TODO list:
- remove quotes from non-reserved identifiers and make reserved identifiers use dba func for quoting
- Rewrite search queries for better results (both MySQL and Postgres)
409 lines
11 KiB
PHP
409 lines
11 KiB
PHP
<?php /** @file */
|
|
|
|
require_once('boot.php');
|
|
require_once('include/cli_startup.php');
|
|
|
|
|
|
function poller_run($argv, $argc){
|
|
|
|
cli_startup();
|
|
|
|
$a = get_app();
|
|
|
|
$maxsysload = intval(get_config('system','maxloadavg'));
|
|
if($maxsysload < 1)
|
|
$maxsysload = 50;
|
|
if(function_exists('sys_getloadavg')) {
|
|
$load = sys_getloadavg();
|
|
if(intval($load[0]) > $maxsysload) {
|
|
logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.');
|
|
return;
|
|
}
|
|
}
|
|
|
|
$interval = intval(get_config('system','poll_interval'));
|
|
if(! $interval)
|
|
$interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval')));
|
|
|
|
// Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it.
|
|
$lockfile = 'store/[data]/poller';
|
|
if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600))) {
|
|
logger("poller: Already running");
|
|
return;
|
|
}
|
|
|
|
// Create a lockfile. Needs two vars, but $x doesn't need to contain anything.
|
|
file_put_contents($lockfile, $x);
|
|
|
|
logger('poller: start');
|
|
|
|
// run queue delivery process in the background
|
|
|
|
proc_run('php',"include/queue.php");
|
|
|
|
|
|
// expire any expired mail
|
|
|
|
q("delete from mail where expires != '%s' and expires < %s ",
|
|
dbesc(NULL_DATE),
|
|
db_utcnow()
|
|
);
|
|
|
|
// expire any expired items
|
|
|
|
$r = q("select id from item where expires != '%s' and expires < %s
|
|
and not ( item_restrict & %d )>0 ",
|
|
dbesc(NULL_DATE),
|
|
db_utcnow(),
|
|
intval(ITEM_DELETED)
|
|
);
|
|
if($r) {
|
|
require_once('include/items.php');
|
|
foreach($r as $rr)
|
|
drop_item($rr['id'],false);
|
|
}
|
|
|
|
|
|
// Ensure that every channel pings a directory server once a month. This way we can discover
|
|
// channels and sites that quietly vanished and prevent the directory from accumulating stale
|
|
// or dead entries.
|
|
|
|
$r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s",
|
|
db_utcnow(), db_quoteinterval('30 DAY')
|
|
);
|
|
if($r) {
|
|
foreach($r as $rr) {
|
|
proc_run('php','include/directory.php',$rr['channel_id'],'force');
|
|
if($interval)
|
|
@time_sleep_until(microtime(true) + (float) $interval);
|
|
}
|
|
}
|
|
|
|
// publish any applicable items that were set to be published in the future
|
|
// (time travel posts)
|
|
|
|
$r = q("select id from item where ( item_restrict & %d )>0 and created <= %s ",
|
|
intval(ITEM_DELAYED_PUBLISH),
|
|
db_utcnow()
|
|
);
|
|
if($r) {
|
|
foreach($r as $rr) {
|
|
$x = q("update item set item_restrict = ( item_restrict & ~%d ) where id = %d",
|
|
intval(ITEM_DELAYED_PUBLISH),
|
|
intval($rr['id'])
|
|
);
|
|
if($x) {
|
|
proc_run('php','include/notifier.php','wall-new',$rr['id']);
|
|
}
|
|
}
|
|
}
|
|
|
|
$abandon_days = intval(get_config('system','account_abandon_days'));
|
|
if($abandon_days < 1)
|
|
$abandon_days = 0;
|
|
|
|
|
|
// once daily run birthday_updates and then expire in background
|
|
|
|
// FIXME: add birthday updates, both locally and for xprof for use
|
|
// by directory servers
|
|
|
|
$d1 = intval(get_config('system','last_expire_day'));
|
|
$d2 = intval(datetime_convert('UTC','UTC','now','d'));
|
|
|
|
// Allow somebody to staggger daily activities if they have more than one site on their server,
|
|
// or if it happens at an inconvenient (busy) hour.
|
|
|
|
$h1 = intval(get_config('system','cron_hour'));
|
|
$h2 = intval(datetime_convert('UTC','UTC','now','G'));
|
|
|
|
$dirmode = get_config('system','directory_mode');
|
|
|
|
/**
|
|
* Cron Daily
|
|
*
|
|
* Actions in the following block are executed once per day, not on every poller run
|
|
*
|
|
*/
|
|
|
|
if(($d2 != $d1) && ($h1 == $h2)) {
|
|
|
|
require_once('include/dir_fns.php');
|
|
check_upstream_directory();
|
|
|
|
call_hooks('cron_daily',datetime_convert());
|
|
|
|
|
|
$d3 = intval(datetime_convert('UTC','UTC','now','N'));
|
|
if($d3 == 7) {
|
|
|
|
/**
|
|
* Cron Weekly
|
|
*
|
|
* Actions in the following block are executed once per day only on Sunday (once per week).
|
|
*
|
|
*/
|
|
|
|
|
|
call_hooks('cron_weekly',datetime_convert());
|
|
|
|
|
|
|
|
require_once('include/hubloc.php');
|
|
prune_hub_reinstalls();
|
|
|
|
require_once('include/Contact.php');
|
|
mark_orphan_hubsxchans();
|
|
|
|
|
|
/**
|
|
* End Cron Weekly
|
|
*/
|
|
}
|
|
|
|
update_birthdays();
|
|
|
|
//update statistics in config
|
|
require_once('include/statistics_fns.php');
|
|
update_channels_total_stat();
|
|
update_channels_active_halfyear_stat();
|
|
update_channels_active_monthly_stat();
|
|
update_local_posts_stat();
|
|
|
|
// expire any read notifications over a month old
|
|
|
|
q("delete from notify where seen = 1 and date < %s - INTERVAL %s",
|
|
db_utcnow(), db_quoteinterval('30 DAY')
|
|
);
|
|
|
|
// expire any expired accounts
|
|
downgrade_accounts();
|
|
|
|
// If this is a directory server, request a sync with an upstream
|
|
// directory at least once a day, up to once every poll interval.
|
|
// Pull remote changes and push local changes.
|
|
// potential issue: how do we keep from creating an endless update loop?
|
|
|
|
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
|
require_once('include/dir_fns.php');
|
|
sync_directories($dirmode);
|
|
}
|
|
|
|
set_config('system','last_expire_day',$d2);
|
|
|
|
proc_run('php','include/expire.php');
|
|
proc_run('php','include/cli_suggest.php');
|
|
|
|
require_once('include/hubloc.php');
|
|
remove_obsolete_hublocs();
|
|
|
|
/**
|
|
* End Cron Daily
|
|
*/
|
|
}
|
|
|
|
// update any photos which didn't get imported properly
|
|
// This should be rare
|
|
|
|
$r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = ''
|
|
and xchan_photo_date < %s - INTERVAL %s",
|
|
db_utcnow(), db_quoteinterval('1 DAY')
|
|
);
|
|
if($r) {
|
|
require_once('include/photo/photo_driver.php');
|
|
foreach($r as $rr) {
|
|
$photos = import_profile_photo($rr['xchan_photo_l'],$rr['xchan_hash']);
|
|
$x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s'
|
|
where xchan_hash = '%s'",
|
|
dbesc($photos[0]),
|
|
dbesc($photos[1]),
|
|
dbesc($photos[2]),
|
|
dbesc($photos[3]),
|
|
dbesc($rr['xchan_hash'])
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
// pull in some public posts
|
|
|
|
if(! get_config('system','disable_discover_tab'))
|
|
proc_run('php','include/externals.php');
|
|
|
|
|
|
$manual_id = 0;
|
|
$generation = 0;
|
|
|
|
$force = false;
|
|
$restart = false;
|
|
|
|
if(($argc > 1) && ($argv[1] == 'force'))
|
|
$force = true;
|
|
|
|
if(($argc > 1) && ($argv[1] == 'restart')) {
|
|
$restart = true;
|
|
$generation = intval($argv[2]);
|
|
if(! $generation)
|
|
killme();
|
|
}
|
|
|
|
if(($argc > 1) && intval($argv[1])) {
|
|
$manual_id = intval($argv[1]);
|
|
$force = true;
|
|
}
|
|
|
|
|
|
$sql_extra = (($manual_id) ? " AND abook_id = $manual_id " : "");
|
|
|
|
reload_plugins();
|
|
|
|
$d = datetime_convert();
|
|
|
|
//TODO check to see if there are any cronhooks before wasting a process
|
|
|
|
if(! $restart)
|
|
proc_run('php','include/cronhooks.php');
|
|
|
|
// Only poll from those with suitable relationships
|
|
|
|
$abandon_sql = (($abandon_days)
|
|
? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY'))
|
|
: ''
|
|
);
|
|
|
|
$randfunc = (ACTIVE_DBTYPE == DBTYPE_POSTGRES) ? 'RANDOM()' : 'RAND()';
|
|
|
|
$contacts = q("SELECT abook_id, abook_flags, abook_updated, abook_connected, abook_closeness, abook_xchan, abook_channel
|
|
FROM abook LEFT JOIN account on abook_account = account_id
|
|
$sql_extra
|
|
AND (( abook_flags & %d )>0 OR ( abook_flags = %d ))
|
|
AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc",
|
|
intval(ABOOK_FLAG_HIDDEN|ABOOK_FLAG_PENDING|ABOOK_FLAG_UNCONNECTED|ABOOK_FLAG_FEED),
|
|
intval(0),
|
|
intval(ACCOUNT_OK),
|
|
intval(ACCOUNT_UNVERIFIED) // FIXME
|
|
|
|
);
|
|
|
|
if($contacts) {
|
|
|
|
foreach($contacts as $contact) {
|
|
|
|
$update = false;
|
|
|
|
$t = $contact['abook_updated'];
|
|
$c = $contact['abook_connected'];
|
|
|
|
if($contact['abook_flags'] & ABOOK_FLAG_FEED) {
|
|
$min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes');
|
|
if(! $min)
|
|
$min = intval(get_config('system','minimum_feedcheck_minutes'));
|
|
if(! $min)
|
|
$min = 60;
|
|
$x = datetime_convert('UTC','UTC',"now - $min minutes");
|
|
if($c < $x) {
|
|
proc_run('php','include/onepoll.php',$contact['abook_id']);
|
|
if($interval)
|
|
@time_sleep_until(microtime(true) + (float) $interval);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
|
|
if($c == $t) {
|
|
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
|
|
$update = true;
|
|
}
|
|
else {
|
|
// if we've never connected with them, start the mark for death countdown from now
|
|
|
|
if($c == NULL_DATE) {
|
|
$r = q("update abook set abook_connected = '%s' where abook_id = %d",
|
|
dbesc(datetime_convert()),
|
|
intval($contact['abook_id'])
|
|
);
|
|
$c = datetime_convert();
|
|
$update = true;
|
|
}
|
|
|
|
// He's dead, Jim
|
|
|
|
if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) {
|
|
$n = q("select xchan_network from xchan where xchan_hash = '%s' limit 1",
|
|
dbesc($contact['abook_xchan'])
|
|
);
|
|
if($n && $n[0]['xchan_network'] == 'zot') {
|
|
$r = q("update abook set abook_flags = (abook_flags | %d) where abook_id = %d",
|
|
intval(ABOOK_FLAG_ARCHIVED),
|
|
intval($contact['abook_id'])
|
|
);
|
|
$update = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if($contact['abook_flags'] & ABOOK_FLAG_ARCHIVED) {
|
|
$update = false;
|
|
continue;
|
|
}
|
|
|
|
// might be dead, so maybe don't poll quite so often
|
|
|
|
// recently deceased, so keep up the regular schedule for 3 days
|
|
|
|
if((strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 3 day")) > 0)
|
|
&& (strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 1 day")) > 0))
|
|
$update = true;
|
|
|
|
// After that back off and put them on a morphine drip
|
|
|
|
if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 2 day")) > 0) {
|
|
$update = true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if((! $update) && (! $force))
|
|
continue;
|
|
|
|
proc_run('php','include/onepoll.php',$contact['abook_id']);
|
|
if($interval)
|
|
@time_sleep_until(microtime(true) + (float) $interval);
|
|
|
|
}
|
|
}
|
|
|
|
if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) {
|
|
$r = q("select distinct ud_addr, updates.* from updates where not ( ud_flags & %d )>0 and ud_addr != '' and ( ud_last = '%s' OR ud_last > %s - INTERVAL %s ) group by ud_addr ",
|
|
intval(UPDATE_FLAGS_UPDATED),
|
|
dbesc(NULL_DATE),
|
|
db_utcnow(), db_quoteinterval('7 DAY')
|
|
);
|
|
if($r) {
|
|
foreach($r as $rr) {
|
|
|
|
// If they didn't respond when we attempted before, back off to once a day
|
|
// After 7 days we won't bother anymore
|
|
|
|
if($rr['ud_last'] != NULL_DATE)
|
|
if($rr['ud_last'] > datetime_convert('UTC','UTC', 'now - 1 day'))
|
|
continue;
|
|
proc_run('php','include/onedirsync.php',$rr['ud_id']);
|
|
if($interval)
|
|
@time_sleep_until(microtime(true) + (float) $interval);
|
|
}
|
|
}
|
|
}
|
|
|
|
//All done - clear the lockfile
|
|
@unlink($lockfile);
|
|
|
|
return;
|
|
}
|
|
|
|
if (array_search(__file__,get_included_files())===0){
|
|
poller_run($argv,$argc);
|
|
killme();
|
|
}
|