]> git.mxchange.org Git - friendica.git/blob - src/Worker/DBClean.php
Update DBClean.php
[friendica.git] / src / Worker / DBClean.php
1 <?php
2 /**
3  * @file src/Worker/DBClean.php
4  * @brief The script is called from time to time to clean the database entries and remove orphaned data.
5  */
6
7 namespace Friendica\Worker;
8
9 use Friendica\Core\Config;
10 use Friendica\Core\Worker;
11 use dba;
12
13 require_once 'include/dba.php';
14
15 class DBClean {
16         public static function execute($stage = 0) {
17
18                 if (!Config::get('system', 'dbclean', false)) {
19                         return;
20                 }
21
22                 if ($stage == 0) {
23                         self::forkCleanProcess();
24                 } else {
25                         self::removeOrphans($stage);
26                 }
27         }
28
29         /**
30          * @brief Fork the different DBClean processes
31          */
32         private static function forkCleanProcess() {
33                 // Get the expire days for step 8 and 9
34                 $days = Config::get('system', 'dbclean-expire-days', 0);
35
36                 for ($i = 1; $i <= 10; $i++) {
37                         // Execute the background script for a step when it isn't finished.
38                         // Execute step 8 and 9 only when $days is defined.
39                         if (!Config::get('system', 'finished-dbclean-'.$i, false) && (($i < 8) || ($i > 9) || ($days > 0))) {
40                                 Worker::add(PRIORITY_LOW, 'DBClean', $i);
41                         }
42                 }
43         }
44
45         /**
46          * @brief Remove orphaned database entries
47          * @param integer $stage What should be deleted?
48          *
49          * Values for $stage:
50          * ------------------
51          *  1:  Old global item entries from item table without user copy.
52          *  2:  Items without parents.
53          *  3:  Orphaned data from thread table.
54          *  4:  Orphaned data from notify table.
55          *  5:  Orphaned data from notify-threads table.
56          *  6:  Orphaned data from sign table.
57          *  7:  Orphaned data from term table.
58          *  8:  Expired threads.
59          *  9:  Old global item entries from expired threads.
60          * 10:  Old conversations.
61          */
62         private static function removeOrphans($stage) {
63                 global $db;
64
65                 $count = 0;
66
67                 // We split the deletion in many small tasks
68                 $limit = 1000;
69
70                 // Get the expire days for step 8 and 9
71                 $days = Config::get('system', 'dbclean-expire-days', 0);
72                 $days_unclaimed = Config::get('system', 'dbclean-expire-unclaimed', 90);
73
74                 if ($days_unclaimed == 0) {
75                         $days_unclaimed = $days;
76                 }
77
78                 if ($stage == 1) {
79                         if ($days_unclaimed <= 0) {
80                                 return;
81                         }
82
83                         $last_id = Config::get('system', 'dbclean-last-id-1', 0);
84
85                         logger("Deleting old global item entries from item table without user copy. Last ID: ".$last_id);
86                         $r = dba::p("SELECT `id` FROM `item` WHERE `uid` = 0 AND
87                                                 NOT EXISTS (SELECT `guid` FROM `item` AS `i` WHERE `item`.`guid` = `i`.`guid` AND `i`.`uid` != 0) AND
88                                                 `received` < UTC_TIMESTAMP() - INTERVAL ? DAY AND `id` >= ?
89                                         ORDER BY `id` LIMIT ".intval($limit), $days_unclaimed, $last_id);
90                         $count = dba::num_rows($r);
91                         if ($count > 0) {
92                                 logger("found global item orphans: ".$count);
93                                 while ($orphan = dba::fetch($r)) {
94                                         $last_id = $orphan["id"];
95                                         dba::delete('item', ['id' => $orphan["id"]]);
96                                 }
97                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 1, $last_id);
98                         } else {
99                                 logger("No global item orphans found");
100                         }
101                         dba::close($r);
102                         logger("Done deleting ".$count." old global item entries from item table without user copy. Last ID: ".$last_id);
103
104                         Config::set('system', 'dbclean-last-id-1', $last_id);
105                 } elseif ($stage == 2) {
106                         $last_id = Config::get('system', 'dbclean-last-id-2', 0);
107
108                         logger("Deleting items without parents. Last ID: ".$last_id);
109                         $r = dba::p("SELECT `id` FROM `item`
110                                         WHERE NOT EXISTS (SELECT `id` FROM `item` AS `i` WHERE `item`.`parent` = `i`.`id`)
111                                         AND `id` >= ? ORDER BY `id` LIMIT ".intval($limit), $last_id);
112                         $count = dba::num_rows($r);
113                         if ($count > 0) {
114                                 logger("found item orphans without parents: ".$count);
115                                 while ($orphan = dba::fetch($r)) {
116                                         $last_id = $orphan["id"];
117                                         dba::delete('item', ['id' => $orphan["id"]]);
118                                 }
119                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 2, $last_id);
120                         } else {
121                                 logger("No item orphans without parents found");
122                         }
123                         dba::close($r);
124                         logger("Done deleting ".$count." items without parents. Last ID: ".$last_id);
125
126                         Config::set('system', 'dbclean-last-id-2', $last_id);
127
128                         if ($count < $limit) {
129                                 Config::set('system', 'finished-dbclean-2', true);
130                         }
131                 } elseif ($stage == 3) {
132                         $last_id = Config::get('system', 'dbclean-last-id-3', 0);
133
134                         logger("Deleting orphaned data from thread table. Last ID: ".$last_id);
135                         $r = dba::p("SELECT `iid` FROM `thread`
136                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `thread`.`iid`) AND `iid` >= ?
137                                         ORDER BY `iid` LIMIT ".intval($limit), $last_id);
138                         $count = dba::num_rows($r);
139                         if ($count > 0) {
140                                 logger("found thread orphans: ".$count);
141                                 while ($orphan = dba::fetch($r)) {
142                                         $last_id = $orphan["iid"];
143                                         dba::delete('thread', ['iid' => $orphan["iid"]]);
144                                 }
145                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 3, $last_id);
146                         } else {
147                                 logger("No thread orphans found");
148                         }
149                         dba::close($r);
150                         logger("Done deleting ".$count." orphaned data from thread table. Last ID: ".$last_id);
151
152                         Config::set('system', 'dbclean-last-id-3', $last_id);
153
154                         if ($count < $limit) {
155                                 Config::set('system', 'finished-dbclean-3', true);
156                         }
157                 } elseif ($stage == 4) {
158                         $last_id = Config::get('system', 'dbclean-last-id-4', 0);
159
160                         logger("Deleting orphaned data from notify table. Last ID: ".$last_id);
161                         $r = dba::p("SELECT `iid`, `id` FROM `notify`
162                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `notify`.`iid`) AND `id` >= ?
163                                         ORDER BY `id` LIMIT ".intval($limit), $last_id);
164                         $count = dba::num_rows($r);
165                         if ($count > 0) {
166                                 logger("found notify orphans: ".$count);
167                                 while ($orphan = dba::fetch($r)) {
168                                         $last_id = $orphan["id"];
169                                         dba::delete('notify', ['iid' => $orphan["iid"]]);
170                                 }
171                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 4, $last_id);
172                         } else {
173                                 logger("No notify orphans found");
174                         }
175                         dba::close($r);
176                         logger("Done deleting ".$count." orphaned data from notify table. Last ID: ".$last_id);
177
178                         Config::set('system', 'dbclean-last-id-4', $last_id);
179
180                         if ($count < $limit) {
181                                 Config::set('system', 'finished-dbclean-4', true);
182                         }
183                 } elseif ($stage == 5) {
184                         $last_id = Config::get('system', 'dbclean-last-id-5', 0);
185
186                         logger("Deleting orphaned data from notify-threads table. Last ID: ".$last_id);
187                         $r = dba::p("SELECT `id` FROM `notify-threads`
188                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `notify-threads`.`master-parent-item`) AND `id` >= ?
189                                         ORDER BY `id` LIMIT ".intval($limit), $last_id);
190                         $count = dba::num_rows($r);
191                         if ($count > 0) {
192                                 logger("found notify-threads orphans: ".$count);
193                                 while ($orphan = dba::fetch($r)) {
194                                         $last_id = $orphan["id"];
195                                         dba::delete('notify-threads', ['id' => $orphan["id"]]);
196                                 }
197                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 5, $last_id);
198                         } else {
199                                 logger("No notify-threads orphans found");
200                         }
201                         dba::close($r);
202                         logger("Done deleting ".$count." orphaned data from notify-threads table. Last ID: ".$last_id);
203
204                         Config::set('system', 'dbclean-last-id-5', $last_id);
205
206                         if ($count < $limit) {
207                                 Config::set('system', 'finished-dbclean-5', true);
208                         }
209                 } elseif ($stage == 6) {
210                         $last_id = Config::get('system', 'dbclean-last-id-6', 0);
211
212                         logger("Deleting orphaned data from sign table. Last ID: ".$last_id);
213                         $r = dba::p("SELECT `iid`, `id` FROM `sign`
214                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `sign`.`iid`) AND `id` >= ?
215                                         ORDER BY `id` LIMIT ".intval($limit), $last_id);
216                         $count = dba::num_rows($r);
217                         if ($count > 0) {
218                                 logger("found sign orphans: ".$count);
219                                 while ($orphan = dba::fetch($r)) {
220                                         $last_id = $orphan["id"];
221                                         dba::delete('sign', ['iid' => $orphan["iid"]]);
222                                 }
223                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 6, $last_id);
224                         } else {
225                                 logger("No sign orphans found");
226                         }
227                         dba::close($r);
228                         logger("Done deleting ".$count." orphaned data from sign table. Last ID: ".$last_id);
229
230                         Config::set('system', 'dbclean-last-id-6', $last_id);
231
232                         if ($count < $limit) {
233                                 Config::set('system', 'finished-dbclean-6', true);
234                         }
235                 } elseif ($stage == 7) {
236                         $last_id = Config::get('system', 'dbclean-last-id-7', 0);
237
238                         logger("Deleting orphaned data from term table. Last ID: ".$last_id);
239                         $r = dba::p("SELECT `oid`, `tid` FROM `term`
240                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `term`.`oid`) AND `tid` >= ?
241                                         ORDER BY `tid` LIMIT ".intval($limit), $last_id);
242                         $count = dba::num_rows($r);
243                         if ($count > 0) {
244                                 logger("found term orphans: ".$count);
245                                 while ($orphan = dba::fetch($r)) {
246                                         $last_id = $orphan["tid"];
247                                         dba::delete('term', ['oid' => $orphan["oid"]]);
248                                 }
249                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 7, $last_id);
250                         } else {
251                                 logger("No term orphans found");
252                         }
253                         dba::close($r);
254                         logger("Done deleting ".$count." orphaned data from term table. Last ID: ".$last_id);
255
256                         Config::set('system', 'dbclean-last-id-7', $last_id);
257
258                         if ($count < $limit) {
259                                 Config::set('system', 'finished-dbclean-7', true);
260                         }
261                 } elseif ($stage == 8) {
262                         if ($days <= 0) {
263                                 return;
264                         }
265
266                         $last_id = Config::get('system', 'dbclean-last-id-8', 0);
267
268                         logger("Deleting expired threads. Last ID: ".$last_id);
269                         $r = dba::p("SELECT `thread`.`iid` FROM `thread`
270                                         INNER JOIN `contact` ON `thread`.`contact-id` = `contact`.`id` AND NOT `notify_new_posts`
271                                         WHERE `thread`.`received` < UTC_TIMESTAMP() - INTERVAL ? DAY
272                                                 AND NOT `thread`.`mention` AND NOT `thread`.`starred`
273                                                 AND NOT `thread`.`wall` AND NOT `thread`.`origin`
274                                                 AND `thread`.`uid` != 0 AND `thread`.`iid` >= ?
275                                                 AND NOT `thread`.`iid` IN (SELECT `parent` FROM `item`
276                                                                 WHERE (`item`.`starred` OR (`item`.`resource-id` != '')
277                                                                         OR (`item`.`file` != '') OR (`item`.`event-id` != '')
278                                                                         OR (`item`.`attach` != '') OR `item`.`wall` OR `item`.`origin`)
279                                                                         AND `item`.`parent` = `thread`.`iid`)
280                                         ORDER BY `thread`.`iid` LIMIT 1000", $days, $last_id);
281                         $count = dba::num_rows($r);
282                         if ($count > 0) {
283                                 logger("found expired threads: ".$count);
284                                 while ($thread = dba::fetch($r)) {
285                                         $last_id = $thread["iid"];
286                                         dba::delete('thread', ['iid' => $thread["iid"]]);
287                                 }
288                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 8, $last_id);
289                         } else {
290                                 logger("No expired threads found");
291                         }
292                         dba::close($r);
293                         logger("Done deleting ".$count." expired threads. Last ID: ".$last_id);
294
295                         Config::set('system', 'dbclean-last-id-8', $last_id);
296                 } elseif ($stage == 9) {
297                         if ($days <= 0) {
298                                 return;
299                         }
300
301                         $last_id = Config::get('system', 'dbclean-last-id-9', 0);
302                         $till_id = Config::get('system', 'dbclean-last-id-8', 0);
303
304                         logger("Deleting old global item entries from expired threads from ID ".$last_id." to ID ".$till_id);
305                         $r = dba::p("SELECT `id` FROM `item` WHERE `uid` = 0 AND
306                                                 NOT EXISTS (SELECT `guid` FROM `item` AS `i` WHERE `item`.`guid` = `i`.`guid` AND `i`.`uid` != 0) AND
307                                                 `received` < UTC_TIMESTAMP() - INTERVAL 90 DAY AND `id` >= ? AND `id` <= ?
308                                         ORDER BY `id` LIMIT ".intval($limit), $last_id, $till_id);
309                         $count = dba::num_rows($r);
310                         if ($count > 0) {
311                                 logger("found global item entries from expired threads: ".$count);
312                                 while ($orphan = dba::fetch($r)) {
313                                         $last_id = $orphan["id"];
314                                         dba::delete('item', ['id' => $orphan["id"]]);
315                                 }
316                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 9, $last_id);
317                         } else {
318                                 logger("No global item entries from expired threads");
319                         }
320                         dba::close($r);
321                         logger("Done deleting ".$count." old global item entries from expired threads. Last ID: ".$last_id);
322
323                         Config::set('system', 'dbclean-last-id-9', $last_id);
324                 } elseif ($stage == 10) {
325                         $last_id = Config::get('system', 'dbclean-last-id-10', 0);
326                         $days = intval(Config::get('system', 'dbclean_expire_conversation', 90));
327
328                         logger("Deleting old conversations. Last created: ".$last_id);
329                         $r = dba::p("SELECT `received`, `item-uri` FROM `conversation`
330                                         WHERE `received` < UTC_TIMESTAMP() - INTERVAL ? DAY
331                                         ORDER BY `received` LIMIT ?", $days, intval($limit));
332                         $count = dba::num_rows($r);
333                         if ($count > 0) {
334                                 logger("found old conversations: ".$count);
335                                 while ($orphan = dba::fetch($r)) {
336                                         $last_id = $orphan["received"];
337                                         dba::delete('conversation', ['item-uri' => $orphan["item-uri"]]);
338                                 }
339                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 10, $last_id);
340                         } else {
341                                 logger("No old conversations found");
342                         }
343                         dba::close($r);
344                         logger("Done deleting ".$count." conversations. Last created: ".$last_id);
345
346                         Config::set('system', 'dbclean-last-id-10', $last_id);
347                 }
348         }
349 }