diff --git a/syncstorage-mysql/src/diesel_ext.rs b/syncstorage-mysql/src/diesel_ext.rs index 18abb4a2..61824fd1 100644 --- a/syncstorage-mysql/src/diesel_ext.rs +++ b/syncstorage-mysql/src/diesel_ext.rs @@ -3,11 +3,44 @@ use std::{fmt::Debug, marker::PhantomData}; use diesel::{ backend::Backend, insertable::CanInsertInSingleQuery, + mysql::Mysql, query_builder::{AstPass, InsertStatement, QueryFragment, QueryId}, + query_dsl::methods::LockingDsl, result::QueryResult, Expression, QuerySource, RunQueryDsl, }; +/// Emit MySQL <= 5.7's `LOCK IN SHARE MODE` +/// +/// MySQL 8 supports `FOR SHARE` as an alias (which diesel natively supports) +/// This is required for MariaDB which does not provide that alias. +pub trait LockInShareModeDsl { + type Output; + + fn lock_in_share_mode(self) -> Self::Output; +} + +impl LockInShareModeDsl for T +where + T: LockingDsl, +{ + type Output = >::Output; + + fn lock_in_share_mode(self) -> Self::Output { + self.with_lock(LockInShareMode) + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LockInShareMode; + +impl QueryFragment for LockInShareMode { + fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { + out.push_sql(" LOCK IN SHARE MODE"); + Ok(()) + } +} + #[allow(dead_code)] // Not really dead, Rust can't see it. #[derive(Debug, Clone)] pub struct OnDuplicateKeyUpdate( diff --git a/syncstorage-mysql/src/models.rs b/syncstorage-mysql/src/models.rs index 7382d1b8..ddcb73a1 100644 --- a/syncstorage-mysql/src/models.rs +++ b/syncstorage-mysql/src/models.rs @@ -19,6 +19,7 @@ use syncstorage_settings::{Quota, DEFAULT_MAX_TOTAL_RECORDS}; use super::{ batch, + diesel_ext::LockInShareModeDsl, pool::{CollectionCache, Conn}, schema::{bso, collections, user_collections}, DbError, DbResult, @@ -133,7 +134,7 @@ impl MysqlDb { .select(user_collections::modified) .filter(user_collections::user_id.eq(user_id)) .filter(user_collections::collection_id.eq(collection_id)) - .for_share() + .lock_in_share_mode() .first(&mut self.conn) .await .optional()?;