Skip to content

Commit f21af3b

Browse files
authored
Merge pull request #1015 from noplanman/improve_cleanup_command
Improve cleanup command
2 parents 4f70bcd + d7d5a5a commit f21af3b

File tree

4 files changed

+62
-50
lines changed

4 files changed

+62
-50
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
1010
### Added
1111
- Code snippet in `GenericmessageCommand` to keep obsolete service message system commands working.
1212
- Static boolean property `SystemCommand::$execute_deprecated` (must be assigned before handling the request) to try and execute any deprecated system command.
13+
- Improved MySQL DB index for `message` table, making the clean much faster on bigger databases. (Thanks to @damianperez)
14+
- `/cleanup` command now supports dry run which simply outputs all queries that would be run.
1315
### Changed
1416
- Small readme and code fixes / simplifications.
1517
- Upgrade PHPUnit to 8.x and PHPCS to 3.5. For tests now minimum PHP version is 7.2.
@@ -23,6 +25,7 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c
2325
- Boolean value for Polls gets saved correctly in MySQL DB.
2426
- Correctly use `Request::answerInlineQuery` in `InlineQuery::answer`.
2527
- PSR-12 incompatibilities in the codebase.
28+
- Improved and corrected `/cleanup` command.
2629
### Security
2730

2831
## [0.60.0] - 2019-08-16

src/Commands/AdminCommands/CleanupCommand.php

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ class CleanupCommand extends AdminCommand
5252
/**
5353
* @var string
5454
*/
55-
protected $usage = '/cleanup <days> or /cleanup <count> <unit> (e.g. 3 weeks)';
55+
protected $usage = '/cleanup [dry] <days> or /cleanup [dry] <count> <unit> (e.g. 3 weeks)';
5656

5757
/**
5858
* @var string
5959
*/
60-
protected $version = '1.0.0';
60+
protected $version = '1.1.0';
6161

6262
/**
6363
* @var bool
@@ -86,14 +86,16 @@ class CleanupCommand extends AdminCommand
8686
* @var array
8787
*/
8888
protected static $default_clean_older_than = [
89-
'chat' => '365 days',
9089
'callback_query' => '30 days',
90+
'chat' => '365 days',
9191
'chosen_inline_result' => '30 days',
92-
'conversation' => '30 days',
92+
'conversation' => '90 days',
9393
'edited_message' => '30 days',
9494
'inline_query' => '30 days',
9595
'message' => '30 days',
96+
'poll' => '90 days',
9697
'request_limiter' => '1 minute',
98+
'shipping_query' => '90 days',
9799
'telegram_update' => '30 days',
98100
'user' => '365 days',
99101
'user_chat' => '365 days',
@@ -163,46 +165,46 @@ private function getQueries($settings)
163165
AND `chat_id` NOT IN (
164166
SELECT `id`
165167
FROM `%4$s`
166-
WHERE `chat_id` = `%4$s`.`id`
167-
AND `updated_at` < \'%1$s\'
168+
WHERE `%3$s`.`chat_id` = `id`
169+
AND `updated_at` < \'%2$s\'
168170
)
169171
AND (
170172
`message_id` IS NOT NULL
171173
AND `message_id` IN (
172-
SELECT f.id
173-
FROM `%5$s` f
174+
SELECT `id`
175+
FROM `%5$s`
174176
WHERE `date` < \'%2$s\'
175177
)
176178
)
177179
OR (
178180
`edited_message_id` IS NOT NULL
179181
AND `edited_message_id` IN (
180-
SELECT f.id
181-
FROM `%6$s` f
182+
SELECT `id`
183+
FROM `%6$s`
182184
WHERE `edit_date` < \'%2$s\'
183185
)
184186
)
185187
OR (
186188
`inline_query_id` IS NOT NULL
187189
AND `inline_query_id` IN (
188-
SELECT f.id
189-
FROM `%7$s` f
190+
SELECT `id`
191+
FROM `%7$s`
190192
WHERE `created_at` < \'%2$s\'
191193
)
192194
)
193195
OR (
194196
`chosen_inline_result_id` IS NOT NULL
195197
AND `chosen_inline_result_id` IN (
196-
SELECT f.id
197-
FROM `%8$s` f
198+
SELECT `id`
199+
FROM `%8$s`
198200
WHERE `created_at` < \'%2$s\'
199201
)
200202
)
201203
OR (
202204
`callback_query_id` IS NOT NULL
203205
AND `callback_query_id` IN (
204-
SELECT f.id
205-
FROM `%9$s` f
206+
SELECT `id`
207+
FROM `%9$s`
206208
WHERE `created_at` < \'%2$s\'
207209
)
208210
)
@@ -223,8 +225,8 @@ private function getQueries($settings)
223225
$queries[] = sprintf(
224226
'DELETE FROM `%1$s`
225227
WHERE `user_id` IN (
226-
SELECT f.id
227-
FROM `%2$s` f
228+
SELECT `id`
229+
FROM `%2$s`
228230
WHERE `updated_at` < \'%3$s\'
229231
)
230232
',
@@ -239,7 +241,9 @@ private function getQueries($settings)
239241
'user' => ['table' => TB_USER, 'field' => 'updated_at'],
240242
'chat' => ['table' => TB_CHAT, 'field' => 'updated_at'],
241243
'conversation' => ['table' => TB_CONVERSATION, 'field' => 'updated_at'],
244+
'poll' => ['table' => TB_POLL, 'field' => 'created_at'],
242245
'request_limiter' => ['table' => TB_REQUEST_LIMITER, 'field' => 'created_at'],
246+
'shipping_query' => ['table' => TB_SHIPPING_QUERY, 'field' => 'created_at'],
243247
];
244248

245249
foreach (array_intersect(array_keys($simple_tables), $tables_to_clean) as $table_to_clean) {
@@ -345,16 +349,7 @@ private function getQueries($settings)
345349
*/
346350
public function executeNoDb()
347351
{
348-
$message = $this->getMessage();
349-
$chat_id = $message->getChat()->getId();
350-
351-
$data = [
352-
'chat_id' => $chat_id,
353-
'parse_mode' => 'Markdown',
354-
'text' => '*No database connection!*',
355-
];
356-
357-
return Request::sendMessage($data);
352+
return $this->replyToChat('*No database connection!*', ['parse_mode' => 'Markdown']);
358353
}
359354

360355
/**
@@ -366,30 +361,36 @@ public function executeNoDb()
366361
public function execute()
367362
{
368363
$message = $this->getMessage();
369-
$user_id = $message->getFrom()->getId();
370364
$text = $message->getText(true);
371365

372-
$data = [
373-
'chat_id' => $user_id,
374-
'parse_mode' => 'Markdown',
375-
];
366+
// Dry run?
367+
$dry_run = strpos($text, 'dry') !== false;
368+
$text = trim(str_replace('dry', '', $text));
376369

377370
$settings = $this->getSettings($text);
378371
$queries = $this->getQueries($settings);
379372

373+
if ($dry_run) {
374+
return $this->replyToUser('Queries:' . PHP_EOL . implode(PHP_EOL, $queries));
375+
}
376+
380377
$infos = [];
381378
foreach ($settings['tables_to_clean'] as $table) {
382-
$info = '*' . $table . '*';
379+
$info = "*{$table}*";
383380

384381
if (isset($settings['clean_older_than'][$table])) {
385-
$info .= ' (' . $settings['clean_older_than'][$table] . ')';
382+
$info .= " ({$settings['clean_older_than'][$table]})";
386383
}
387384

388385
$infos[] = $info;
389386
}
390387

391-
$data['text'] = 'Cleaning up tables:' . PHP_EOL . implode(PHP_EOL, $infos);
388+
$data = [
389+
'chat_id' => $message->getFrom()->getId(),
390+
'parse_mode' => 'Markdown',
391+
];
392392

393+
$data['text'] = 'Cleaning up tables:' . PHP_EOL . implode(PHP_EOL, $infos);
393394
Request::sendMessage($data);
394395

395396
$rows = 0;
@@ -398,27 +399,30 @@ public function execute()
398399
$pdo->beginTransaction();
399400

400401
foreach ($queries as $query) {
401-
if ($dbq = $pdo->query($query)) {
402+
// Delete in chunks to not block / improve speed on big tables.
403+
$query .= ' LIMIT 10000';
404+
while ($dbq = $pdo->query($query)) {
405+
if ($dbq->rowCount() === 0) {
406+
continue 2;
407+
}
402408
$rows += $dbq->rowCount();
403-
} else {
404-
TelegramLog::error('Error while executing query: ' . $query);
405409
}
410+
411+
TelegramLog::error('Error while executing query: ' . $query);
406412
}
407413

408-
$pdo->commit(); // commit changes to the database and end transaction
409-
} catch (PDOException $e) {
410-
$pdo->rollBack(); // rollback changes on exception (useful if you want to track down error - you can't replicate it when some of the data is already deleted...)
414+
// commit changes to the database and end transaction
415+
$pdo->commit();
411416

417+
$data['text'] = "*Database cleanup done!* _(removed {$rows} rows)_";
418+
} catch (PDOException $e) {
412419
$data['text'] = '*Database cleanup failed!* _(check your error logs)_';
413-
Request::sendMessage($data);
414420

415-
throw new TelegramException($e->getMessage());
416-
}
421+
// rollback changes on exception
422+
// useful if you want to track down error you can't replicate it when some of the data is already deleted
423+
$pdo->rollBack();
417424

418-
if ($rows > 0) {
419-
$data['text'] = '*Database cleanup done!* _(removed ' . $rows . ' rows)_';
420-
} else {
421-
$data['text'] = '*No data to clean!*';
425+
TelegramLog::error($e->getMessage());
422426
}
423427

424428
return Request::sendMessage($data);

structure.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ CREATE TABLE IF NOT EXISTS `telegram_update` (
230230
`poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New poll state. Bots receive only updates about polls, which are sent or stopped by the bot',
231231

232232
PRIMARY KEY (`id`),
233-
KEY `message_id` (`chat_id`, `message_id`),
233+
KEY `message_id` (`message_id`),
234+
KEY `chat_message_id` (`chat_id`, `message_id`),
234235
KEY `edited_message_id` (`edited_message_id`),
235236
KEY `channel_post_id` (`channel_post_id`),
236237
KEY `edited_channel_post_id` (`edited_channel_post_id`),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ALTER TABLE `message`
2+
DROP KEY `message_id`,
3+
ADD KEY `message_id` (`message_id`),
4+
ADD KEY `chat_message_id` (`chat_id`, `message_id`);

0 commit comments

Comments
 (0)