Skip to content

Commit 04a2f24

Browse files
authored
Merge pull request #7665 from nextcloud/fix/delete-old-sessions
Fix: Delete old sessions in cleanup cron
2 parents 0f8003f + 3665ce7 commit 04a2f24

4 files changed

Lines changed: 81 additions & 0 deletions

File tree

lib/Cron/Cleanup.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ protected function run($argument): void {
5454
$removedSessions = $this->sessionService->removeInactiveSessionsWithoutSteps();
5555
$this->logger->debug('Removed ' . $removedSessions . ' inactive sessions');
5656

57+
$this->logger->debug('Run cleanup job for old sessions');
58+
$removedOldSessions = $this->sessionService->removeOldSessions();
59+
$this->logger->debug('Removed ' . $removedOldSessions . ' old sessions');
60+
5761
$this->logger->debug('Run cleanup job for obsolete documents folders');
5862
$this->documentService->cleanupOldDocumentsFolders();
5963
}

lib/Db/SessionMapper.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,42 @@ public function deleteInactiveWithoutSteps(?int $documentId = null): int {
142142
return $deletedCount;
143143
}
144144

145+
public function deleteOldSessions(int $ageInSeconds): int {
146+
$startTime = microtime(true);
147+
$maxExecutionSeconds = 30;
148+
$batchSize = 1000;
149+
$deletedCount = 0;
150+
$ageThreshold = time() - $ageInSeconds;
151+
152+
do {
153+
$oldSessionsQb = $this->db->getQueryBuilder();
154+
$result = $oldSessionsQb->select('id')
155+
->from('text_sessions')
156+
->where($oldSessionsQb->expr()->lt('last_contact', $oldSessionsQb->createNamedParameter($ageThreshold)))
157+
->setMaxResults($batchSize)
158+
->executeQuery();
159+
160+
$sessionIds = array_map(function ($row) {
161+
return (int)$row['id'];
162+
}, $result->fetchAll());
163+
$result->closeCursor();
164+
165+
if (empty($sessionIds)) {
166+
break;
167+
}
168+
169+
$deleteSessionsQb = $this->db->getQueryBuilder();
170+
$batchDeleted = $deleteSessionsQb->delete('text_sessions')
171+
->where($deleteSessionsQb->expr()->in('id', $deleteSessionsQb->createParameter('ids'), IQueryBuilder::PARAM_INT_ARRAY))
172+
->setParameter('ids', $sessionIds, IQueryBuilder::PARAM_INT_ARRAY)
173+
->executeStatement();
174+
175+
$deletedCount += $batchDeleted;
176+
} while ((microtime(true) - $startTime) < $maxExecutionSeconds);
177+
178+
return $deletedCount;
179+
}
180+
145181
public function deleteByDocumentId(int $documentId): int {
146182
$qb = $this->db->getQueryBuilder();
147183
$qb->delete($this->getTableName())

lib/Service/SessionService.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ public function removeInactiveSessionsWithoutSteps(?int $documentId = null): int
138138
return $this->sessionMapper->deleteInactiveWithoutSteps($documentId);
139139
}
140140

141+
public function removeOldSessions(int $ageInSeconds = 7776000): int {
142+
return $this->sessionMapper->deleteOldSessions($ageInSeconds);
143+
}
144+
141145
public function getSession(int $documentId, int $sessionId, string $token): ?Session {
142146
if ($this->session !== null) {
143147
return $this->session;

tests/unit/Db/SessionMapperTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,41 @@ public function testDeleteInactiveWithoutStepsMultiple() {
9898

9999
self::assertCount(0, $this->sessionMapper->findAll(1));
100100
}
101+
102+
public function testDeleteOldSessions() {
103+
$this->stepMapper->deleteAll(1);
104+
$this->sessionMapper->deleteByDocumentId(1);
105+
106+
$fourMonthsAgo = time() - (120 * 24 * 60 * 60);
107+
$oneWeekAgo = time() - (7 * 24 * 60 * 60);
108+
109+
// Create old and recent session
110+
$oldSession = $this->sessionMapper->insert(Session::fromParams([
111+
'userId' => 'admin',
112+
'documentId' => 1,
113+
'lastContact' => $fourMonthsAgo,
114+
'token' => uniqid(),
115+
'color' => '00ff00',
116+
]));
117+
$recentSession = $this->sessionMapper->insert(Session::fromParams([
118+
'userId' => 'admin',
119+
'documentId' => 1,
120+
'lastContact' => $oneWeekAgo,
121+
'token' => uniqid(),
122+
'color' => 'ff0000',
123+
]));
124+
125+
// Verify 2 sessions
126+
self::assertCount(2, $this->sessionMapper->findAll(1));
127+
128+
// Delete sessions older than 90 days
129+
$threeMonths = 90 * 24 * 60 * 60;
130+
$deletedCount = $this->sessionMapper->deleteOldSessions($threeMonths);
131+
self::assertEquals(1, $deletedCount);
132+
133+
// Should have 1 recent session remaining
134+
$remainingSessions = $this->sessionMapper->findAll(1);
135+
self::assertCount(1, $remainingSessions);
136+
self::assertEquals($recentSession->getId(), $remainingSessions[0]->getId());
137+
}
101138
}

0 commit comments

Comments
 (0)