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}