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