Skip to content

Commit 7bada6f

Browse files
committed
fix(db): Auto-unlock mailman sync after 23h
This commit introduces a 23-hour time limit on mailman sync locks. If the mailman sync is running for >23 hours without refreshing its lock, it is presumed to have failed.
1 parent 9f49ce7 commit 7bada6f

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

.env.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,7 @@ PHP_IDE_CONFIG=serverName=database.gewis.nl
9696
# Demo mode credentials (this will prepopulate the credential fields with these values)
9797
DEMO_CREDENTIALS_USERNAME=admin
9898
DEMO_CREDENTIALS_PASSWORD=gewisdbgewis
99+
100+
# Timezone (incl Postfix)
101+
TZ=Europe/Amsterdam
102+
PGTZ=Europe/Amsterdam
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Database\Migrations;
6+
7+
use DateTime;
8+
use Doctrine\DBAL\Schema\Schema;
9+
use Doctrine\Migrations\AbstractMigration;
10+
11+
/**
12+
* phpcs:disable Generic.Files.LineLength.TooLong
13+
* phpcs:disable SlevomatCodingStandard.Functions.RequireMultiLineCall.RequiredMultiLineCall
14+
*/
15+
final class Version20260211203320 extends AbstractMigration
16+
{
17+
public function getDescription(): string
18+
{
19+
return 'Change config item for mailman sync to date value';
20+
}
21+
22+
public function up(Schema $schema): void
23+
{
24+
$this->addSql('UPDATE configitem SET valuebool = null, valuedate = updatedat + interval \'23h\' * valuebool::int WHERE key = \'locked\' AND namespace = \'database_mailman\'');
25+
}
26+
27+
public function down(Schema $schema): void
28+
{
29+
$this->addSql('UPDATE configitem SET valuebool = valuedate > \'' . (new DateTime())->format('Y-m-d H:i:s') . '\', valuedate = null WHERE key = \'locked\' AND namespace = \'database_mailman\'');
30+
}
31+
}

module/Database/src/Service/Mailman.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,29 @@ private function performMailmanRequest(
130130
* Acquire sync lock.
131131
*
132132
* To ensure that the sync between GEWISDB and Mailman is as clean as possible, we need to acquire a global lock on
133-
* the mail list administration. This will prevent (if properly implemented and used) the secretary from modifying
134-
* any mailing list memberships.
133+
* the mail list administration. This will prevent (if properly implemented and used) concurrent syncs from running.
135134
*/
136-
private function acquireSyncLock(int $retries = 3): void
137-
{
135+
private function acquireSyncLock(
136+
int $retries = 3,
137+
bool $renew = false,
138+
): void {
138139
if (0 === $retries) {
139140
throw new RuntimeException('Unable to acquire sync lock for Mailman sync: timeout.');
140141
}
141142

142-
if ($this->isSyncLocked()) {
143+
if ($this->isSyncLocked() && !$renew) {
143144
throw new RuntimeException('Unable to acquire sync lock for Mailman sync: locked by other process.');
144145
}
145146

146-
$this->configService->setConfig(ConfigNamespaces::DatabaseMailman, 'locked', true);
147+
if (!$this->isSyncLocked() && $renew) {
148+
throw new RuntimeException('Unable to renew sync lock for Mailman sync: currently unlocked.');
149+
}
150+
151+
$this->configService->setConfig(
152+
ConfigNamespaces::DatabaseMailman,
153+
'locked',
154+
(new DateTime())->modify('+23 hours'),
155+
);
147156

148157
if ($this->isSyncLocked()) {
149158
return;
@@ -159,15 +168,15 @@ private function acquireSyncLock(int $retries = 3): void
159168
*/
160169
private function releaseSyncLock(): void
161170
{
162-
$this->configService->setConfig(ConfigNamespaces::DatabaseMailman, 'locked', false);
171+
$this->configService->setConfig(ConfigNamespaces::DatabaseMailman, 'locked', new DateTime());
163172
}
164173

165174
/**
166175
* Get state of sync lock.
167176
*/
168177
public function isSyncLocked(): bool
169178
{
170-
return $this->configService->getConfig(ConfigNamespaces::DatabaseMailman, 'locked', false);
179+
return $this->configService->getConfig(ConfigNamespaces::DatabaseMailman, 'locked') > new DateTime();
171180
}
172181

173182
/**
@@ -185,6 +194,7 @@ public function syncMembership(
185194
$lists = $this->mailingListMapper->findAll();
186195

187196
foreach ($lists as $list) {
197+
$this->acquireSyncLock(renew: true);
188198
$this->syncMembershipSingle($list, $output, $dryRun);
189199
}
190200

0 commit comments

Comments
 (0)