]> git.mxchange.org Git - friendica.git/blob - src/Worker/DBClean.php
Delivery of reshares
[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                 // We split the deletion in many small tasks
64                 $limit = Config::get('system', 'dbclean-expire-limit', 1000);
65
66                 // Get the expire days for step 8 and 9
67                 $days = Config::get('system', 'dbclean-expire-days', 0);
68                 $days_unclaimed = Config::get('system', 'dbclean-expire-unclaimed', 90);
69
70                 if ($days_unclaimed == 0) {
71                         $days_unclaimed = $days;
72                 }
73
74                 if ($stage == 1) {
75                         if ($days_unclaimed <= 0) {
76                                 return;
77                         }
78
79                         $last_id = Config::get('system', 'dbclean-last-id-1', 0);
80
81                         Logger::log("Deleting old global item entries from item table without user copy. Last ID: ".$last_id);
82                         $r = DBA::p("SELECT `id` FROM `item` WHERE `uid` = 0 AND
83                                                 NOT EXISTS (SELECT `guid` FROM `item` AS `i` WHERE `item`.`guid` = `i`.`guid` AND `i`.`uid` != 0) AND
84                                                 `received` < UTC_TIMESTAMP() - INTERVAL ? DAY AND `id` >= ?
85                                         ORDER BY `id` LIMIT ?", $days_unclaimed, $last_id, $limit);
86                         $count = DBA::numRows($r);
87                         if ($count > 0) {
88                                 Logger::log("found global item orphans: ".$count);
89                                 while ($orphan = DBA::fetch($r)) {
90                                         $last_id = $orphan["id"];
91                                         DBA::delete('item', ['id' => $orphan["id"]]);
92                                 }
93                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 1, $last_id);
94                         } else {
95                                 Logger::log("No global item orphans found");
96                         }
97                         DBA::close($r);
98                         Logger::log("Done deleting ".$count." old global item entries from item table without user copy. Last ID: ".$last_id);
99
100                         Config::set('system', 'dbclean-last-id-1', $last_id);
101                 } elseif ($stage == 2) {
102                         $last_id = Config::get('system', 'dbclean-last-id-2', 0);
103
104                         Logger::log("Deleting items without parents. Last ID: ".$last_id);
105                         $r = DBA::p("SELECT `id` FROM `item`
106                                         WHERE NOT EXISTS (SELECT `id` FROM `item` AS `i` WHERE `item`.`parent` = `i`.`id`)
107                                         AND `id` >= ? ORDER BY `id` LIMIT ?", $last_id, $limit);
108                         $count = DBA::numRows($r);
109                         if ($count > 0) {
110                                 Logger::log("found item orphans without parents: ".$count);
111                                 while ($orphan = DBA::fetch($r)) {
112                                         $last_id = $orphan["id"];
113                                         DBA::delete('item', ['id' => $orphan["id"]]);
114                                 }
115                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 2, $last_id);
116                         } else {
117                                 Logger::log("No item orphans without parents found");
118                         }
119                         DBA::close($r);
120                         Logger::log("Done deleting ".$count." items without parents. Last ID: ".$last_id);
121
122                         Config::set('system', 'dbclean-last-id-2', $last_id);
123
124                         if ($count < $limit) {
125                                 Config::set('system', 'finished-dbclean-2', true);
126                         }
127                 } elseif ($stage == 3) {
128                         $last_id = Config::get('system', 'dbclean-last-id-3', 0);
129
130                         Logger::log("Deleting orphaned data from thread table. Last ID: ".$last_id);
131                         $r = DBA::p("SELECT `iid` FROM `thread`
132                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `thread`.`iid`) AND `iid` >= ?
133                                         ORDER BY `iid` LIMIT ?", $last_id, $limit);
134                         $count = DBA::numRows($r);
135                         if ($count > 0) {
136                                 Logger::log("found thread orphans: ".$count);
137                                 while ($orphan = DBA::fetch($r)) {
138                                         $last_id = $orphan["iid"];
139                                         DBA::delete('thread', ['iid' => $orphan["iid"]]);
140                                 }
141                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 3, $last_id);
142                         } else {
143                                 Logger::log("No thread orphans found");
144                         }
145                         DBA::close($r);
146                         Logger::log("Done deleting ".$count." orphaned data from thread table. Last ID: ".$last_id);
147
148                         Config::set('system', 'dbclean-last-id-3', $last_id);
149
150                         if ($count < $limit) {
151                                 Config::set('system', 'finished-dbclean-3', true);
152                         }
153                 } elseif ($stage == 4) {
154                         $last_id = Config::get('system', 'dbclean-last-id-4', 0);
155
156                         Logger::log("Deleting orphaned data from notify table. Last ID: ".$last_id);
157                         $r = DBA::p("SELECT `iid`, `id` FROM `notify`
158                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `notify`.`iid`) AND `id` >= ?
159                                         ORDER BY `id` LIMIT ?", $last_id, $limit);
160                         $count = DBA::numRows($r);
161                         if ($count > 0) {
162                                 Logger::log("found notify orphans: ".$count);
163                                 while ($orphan = DBA::fetch($r)) {
164                                         $last_id = $orphan["id"];
165                                         DBA::delete('notify', ['iid' => $orphan["iid"]]);
166                                 }
167                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 4, $last_id);
168                         } else {
169                                 Logger::log("No notify orphans found");
170                         }
171                         DBA::close($r);
172                         Logger::log("Done deleting ".$count." orphaned data from notify table. Last ID: ".$last_id);
173
174                         Config::set('system', 'dbclean-last-id-4', $last_id);
175
176                         if ($count < $limit) {
177                                 Config::set('system', 'finished-dbclean-4', true);
178                         }
179                 } elseif ($stage == 5) {
180                         $last_id = Config::get('system', 'dbclean-last-id-5', 0);
181
182                         Logger::log("Deleting orphaned data from notify-threads table. Last ID: ".$last_id);
183                         $r = DBA::p("SELECT `id` FROM `notify-threads`
184                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `notify-threads`.`master-parent-item`) AND `id` >= ?
185                                         ORDER BY `id` LIMIT ?", $last_id, $limit);
186                         $count = DBA::numRows($r);
187                         if ($count > 0) {
188                                 Logger::log("found notify-threads orphans: ".$count);
189                                 while ($orphan = DBA::fetch($r)) {
190                                         $last_id = $orphan["id"];
191                                         DBA::delete('notify-threads', ['id' => $orphan["id"]]);
192                                 }
193                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 5, $last_id);
194                         } else {
195                                 Logger::log("No notify-threads orphans found");
196                         }
197                         DBA::close($r);
198                         Logger::log("Done deleting ".$count." orphaned data from notify-threads table. Last ID: ".$last_id);
199
200                         Config::set('system', 'dbclean-last-id-5', $last_id);
201
202                         if ($count < $limit) {
203                                 Config::set('system', 'finished-dbclean-5', true);
204                         }
205                 } elseif ($stage == 6) {
206                         $last_id = Config::get('system', 'dbclean-last-id-6', 0);
207
208                         Logger::log("Deleting orphaned data from sign table. Last ID: ".$last_id);
209                         $r = DBA::p("SELECT `iid`, `id` FROM `sign`
210                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `sign`.`iid`) AND `id` >= ?
211                                         ORDER BY `id` LIMIT ?", $last_id, $limit);
212                         $count = DBA::numRows($r);
213                         if ($count > 0) {
214                                 Logger::log("found sign orphans: ".$count);
215                                 while ($orphan = DBA::fetch($r)) {
216                                         $last_id = $orphan["id"];
217                                         DBA::delete('sign', ['iid' => $orphan["iid"]]);
218                                 }
219                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 6, $last_id);
220                         } else {
221                                 Logger::log("No sign orphans found");
222                         }
223                         DBA::close($r);
224                         Logger::log("Done deleting ".$count." orphaned data from sign table. Last ID: ".$last_id);
225
226                         Config::set('system', 'dbclean-last-id-6', $last_id);
227
228                         if ($count < $limit) {
229                                 Config::set('system', 'finished-dbclean-6', true);
230                         }
231                 } elseif ($stage == 7) {
232                         $last_id = Config::get('system', 'dbclean-last-id-7', 0);
233
234                         Logger::log("Deleting orphaned data from term table. Last ID: ".$last_id);
235                         $r = DBA::p("SELECT `oid`, `tid` FROM `term`
236                                         WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `term`.`oid`) AND `tid` >= ?
237                                         ORDER BY `tid` LIMIT ?", $last_id, $limit);
238                         $count = DBA::numRows($r);
239                         if ($count > 0) {
240                                 Logger::log("found term orphans: ".$count);
241                                 while ($orphan = DBA::fetch($r)) {
242                                         $last_id = $orphan["tid"];
243                                         DBA::delete('term', ['oid' => $orphan["oid"]]);
244                                 }
245                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 7, $last_id);
246                         } else {
247                                 Logger::log("No term orphans found");
248                         }
249                         DBA::close($r);
250                         Logger::log("Done deleting ".$count." orphaned data from term table. Last ID: ".$last_id);
251
252                         Config::set('system', 'dbclean-last-id-7', $last_id);
253
254                         if ($count < $limit) {
255                                 Config::set('system', 'finished-dbclean-7', true);
256                         }
257                 } elseif ($stage == 8) {
258                         if ($days <= 0) {
259                                 return;
260                         }
261
262                         $last_id = Config::get('system', 'dbclean-last-id-8', 0);
263
264                         Logger::log("Deleting expired threads. Last ID: ".$last_id);
265                         $r = DBA::p("SELECT `thread`.`iid` FROM `thread`
266                                         INNER JOIN `contact` ON `thread`.`contact-id` = `contact`.`id` AND NOT `notify_new_posts`
267                                         WHERE `thread`.`received` < UTC_TIMESTAMP() - INTERVAL ? DAY
268                                                 AND NOT `thread`.`mention` AND NOT `thread`.`starred`
269                                                 AND NOT `thread`.`wall` AND NOT `thread`.`origin`
270                                                 AND `thread`.`uid` != 0 AND `thread`.`iid` >= ?
271                                                 AND NOT `thread`.`iid` IN (SELECT `parent` FROM `item`
272                                                                 WHERE (`item`.`starred` OR (`item`.`resource-id` != '')
273                                                                         OR (`item`.`file` != '') OR (`item`.`event-id` != '')
274                                                                         OR (`item`.`attach` != '') OR `item`.`wall` OR `item`.`origin`)
275                                                                         AND `item`.`parent` = `thread`.`iid`)
276                                         ORDER BY `thread`.`iid` LIMIT ?", $days, $last_id, $limit);
277                         $count = DBA::numRows($r);
278                         if ($count > 0) {
279                                 Logger::log("found expired threads: ".$count);
280                                 while ($thread = DBA::fetch($r)) {
281                                         $last_id = $thread["iid"];
282                                         DBA::delete('thread', ['iid' => $thread["iid"]]);
283                                 }
284                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 8, $last_id);
285                         } else {
286                                 Logger::log("No expired threads found");
287                         }
288                         DBA::close($r);
289                         Logger::log("Done deleting ".$count." expired threads. Last ID: ".$last_id);
290
291                         Config::set('system', 'dbclean-last-id-8', $last_id);
292                 } elseif ($stage == 9) {
293                         if ($days <= 0) {
294                                 return;
295                         }
296
297                         $last_id = Config::get('system', 'dbclean-last-id-9', 0);
298                         $till_id = Config::get('system', 'dbclean-last-id-8', 0);
299
300                         Logger::log("Deleting old global item entries from expired threads from ID ".$last_id." to ID ".$till_id);
301                         $r = DBA::p("SELECT `id` FROM `item` WHERE `uid` = 0 AND
302                                                 NOT EXISTS (SELECT `guid` FROM `item` AS `i` WHERE `item`.`guid` = `i`.`guid` AND `i`.`uid` != 0) AND
303                                                 `received` < UTC_TIMESTAMP() - INTERVAL 90 DAY AND `id` >= ? AND `id` <= ?
304                                         ORDER BY `id` LIMIT ?", $last_id, $till_id, $limit);
305                         $count = DBA::numRows($r);
306                         if ($count > 0) {
307                                 Logger::log("found global item entries from expired threads: ".$count);
308                                 while ($orphan = DBA::fetch($r)) {
309                                         $last_id = $orphan["id"];
310                                         DBA::delete('item', ['id' => $orphan["id"]]);
311                                 }
312                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 9, $last_id);
313                         } else {
314                                 Logger::log("No global item entries from expired threads");
315                         }
316                         DBA::close($r);
317                         Logger::log("Done deleting ".$count." old global item entries from expired threads. Last ID: ".$last_id);
318
319                         Config::set('system', 'dbclean-last-id-9', $last_id);
320                 } elseif ($stage == 10) {
321                         $last_id = Config::get('system', 'dbclean-last-id-10', 0);
322                         $days = intval(Config::get('system', 'dbclean_expire_conversation', 90));
323
324                         Logger::log("Deleting old conversations. Last created: ".$last_id);
325                         $r = DBA::p("SELECT `received`, `item-uri` FROM `conversation`
326                                         WHERE `received` < UTC_TIMESTAMP() - INTERVAL ? DAY
327                                         ORDER BY `received` LIMIT ?", $days, $limit);
328                         $count = DBA::numRows($r);
329                         if ($count > 0) {
330                                 Logger::log("found old conversations: ".$count);
331                                 while ($orphan = DBA::fetch($r)) {
332                                         $last_id = $orphan["received"];
333                                         DBA::delete('conversation', ['item-uri' => $orphan["item-uri"]]);
334                                 }
335                                 Worker::add(PRIORITY_MEDIUM, 'DBClean', 10, $last_id);
336                         } else {
337                                 Logger::log("No old conversations found");
338                         }
339                         DBA::close($r);
340                         Logger::log("Done deleting ".$count." conversations. Last created: ".$last_id);
341
342                         Config::set('system', 'dbclean-last-id-10', $last_id);
343                 }
344         }
345 }