syn2mas/mas_writer/
locking.rs

1// Copyright 2024 New Vector Ltd.
2//
3// SPDX-License-Identifier: AGPL-3.0-only
4// Please see LICENSE in the repository root for full details.
5
6use std::sync::LazyLock;
7
8use sqlx::{
9    Either, PgConnection,
10    postgres::{PgAdvisoryLock, PgAdvisoryLockGuard},
11};
12
13static SYN2MAS_ADVISORY_LOCK: LazyLock<PgAdvisoryLock> =
14    LazyLock::new(|| PgAdvisoryLock::new("syn2mas-maswriter"));
15
16/// A wrapper around a Postgres connection which holds a session-wide advisory
17/// lock preventing concurrent access by other syn2mas instances.
18pub struct LockedMasDatabase {
19    inner: PgAdvisoryLockGuard<'static, PgConnection>,
20}
21
22impl LockedMasDatabase {
23    /// Attempts to lock the MAS database against concurrent access by other
24    /// syn2mas instances.
25    ///
26    /// If the lock can be acquired, returns a `LockedMasDatabase`.
27    /// If the lock cannot be acquired, returns the connection back to the
28    /// caller wrapped in `Either::Right`.
29    ///
30    /// # Errors
31    ///
32    /// Errors are returned for underlying database errors.
33    pub async fn try_new(
34        mas_connection: PgConnection,
35    ) -> Result<Either<Self, PgConnection>, sqlx::Error> {
36        SYN2MAS_ADVISORY_LOCK
37            .try_acquire(mas_connection)
38            .await
39            .map(|either| match either {
40                Either::Left(inner) => Either::Left(LockedMasDatabase { inner }),
41                Either::Right(unlocked) => Either::Right(unlocked),
42            })
43    }
44
45    /// Releases the advisory lock on the MAS database, returning the underlying
46    /// connection.
47    ///
48    /// # Errors
49    ///
50    /// Errors are returned for underlying database errors.
51    pub async fn unlock(self) -> Result<PgConnection, sqlx::Error> {
52        self.inner.release_now().await
53    }
54}
55
56impl AsMut<PgConnection> for LockedMasDatabase {
57    fn as_mut(&mut self) -> &mut PgConnection {
58        self.inner.as_mut()
59    }
60}