idiff --git a/syncstorage-mysql/src/diesel_ext.rs b/syncstorage-mysql/src/diesel_ext.rs index 18abb4a..fe52470 100644 --- a/syncstorage-mysql/src/diesel_ext.rs +++ b/syncstorage-mysql/src/diesel_ext.rs @@ -3,11 +3,43 @@ 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) +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 1114f21..fb54649 100644 --- a/syncstorage-mysql/src/models.rs +++ b/syncstorage-mysql/src/models.rs @@ -25,6 +25,7 @@ use syncstorage_settings::{Quota, DEFAULT_MAX_TOTAL_RECORDS}; use super::{ batch, + diesel_ext::LockInShareModeDsl, error::DbError, pool::{CollectionCache, Conn}, schema::{bso, collections, user_collections}, @@ -179,7 +180,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.write()?) .optional()?; if let Some(modified) = modified {