Внедрение дизельного соединения в промежуточное программное обеспечение Iron

При написании своих тестов я хотел бы иметь возможность внедрить соединение в запрос, чтобы я мог заключить весь тестовый пример в транзакцию (даже если в тестовом примере имеется более одного запроса).

Я пытался сделать это с помощью BeforeMiddleware который я могу связать в моих тестовых случаях, чтобы вставить соединение, как таковое:

pub type DatabaseConnection = PooledConnection<ConnectionManager<PgConnection>>;

pub struct DatabaseOverride {
    conn: DatabaseConnection,
}

impl BeforeMiddleware for DatabaseOverride {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        req.extensions_mut().entry::<DatabaseOverride>().or_insert(self.conn);
        Ok(())
    }
}

Тем не менее, я сталкиваюсь с ошибкой компиляции при попытке сделать это:

error: the trait bound `std::rc::Rc<diesel::pg::connection::raw::RawConnection>: std::marker::Sync` is not satisfied [E0277]
impl BeforeMiddleware for DatabaseOverride {
     ^~~~~~~~~~~~~~~~
help: run `rustc --explain E0277` to see a detailed explanation
note: `std::rc::Rc<diesel::pg::connection::raw::RawConnection>` cannot be shared between threads safely
note: required because it appears within the type `diesel::pg::PgConnection`
note: required because it appears within the type `r2d2::Conn<diesel::pg::PgConnection>`
note: required because it appears within the type `std::option::Option<r2d2::Conn<diesel::pg::PgConnection>>`
note: required because it appears within the type `r2d2::PooledConnection<r2d2_diesel::ConnectionManager<diesel::pg::PgConnection>>`
note: required because it appears within the type `utility::db::DatabaseOverride`
note: required by `iron::BeforeMiddleware`

error: the trait bound `std::cell::Cell<i32>: std::marker::Sync` is not satisfied [E0277]
impl BeforeMiddleware for DatabaseOverride {
     ^~~~~~~~~~~~~~~~
help: run `rustc --explain E0277` to see a detailed explanation
note: `std::cell::Cell<i32>` cannot be shared between threads safely
note: required because it appears within the type `diesel::pg::PgConnection`
note: required because it appears within the type `r2d2::Conn<diesel::pg::PgConnection>`
note: required because it appears within the type `std::option::Option<r2d2::Conn<diesel::pg::PgConnection>>`
note: required because it appears within the type `r2d2::PooledConnection<r2d2_diesel::ConnectionManager<diesel::pg::PgConnection>>`
note: required because it appears within the type `utility::db::DatabaseOverride`
note: required by `iron::BeforeMiddleware`

Есть ли способ обойти это с дизельными двигателями? Я нашел несколько примеров на Github, чтобы сделать это с помощью pg ящик, но я хотел бы продолжать использовать дизель.

2 ответа

Этот ответ, безусловно, решит проблему, но он не оптимален. Как уже упоминалось, вы не можете использовать одно соединение, так как оно не безопасно для потоков. Однако, оборачивая его в Mutex делает потокобезопасным, это заставит все потоки сервера использовать одно соединение. Вместо этого вы хотите использовать пул соединений.

Вы можете сделать это с помощью ящиков r2d2 и r2d2-дизель. Это создаст несколько соединений по мере необходимости и повторно использует их, когда это возможно, потокобезопасным способом.

Поскольку для воспроизведения вашей проблемы недостаточно кода, я сделал это:

use std::cell::Cell;

trait Middleware: Sync {}

struct Unsharable(Cell<bool>);

impl Middleware for Unsharable {}

fn main() {}

который имеет ту же ошибку:

error: the trait bound `std::cell::Cell<bool>: std::marker::Sync` is not satisfied [E0277]
impl Middleware for Unsharable {}
     ^~~~~~~~~~
help: run `rustc --explain E0277` to see a detailed explanation
note: `std::cell::Cell<bool>` cannot be shared between threads safely
note: required because it appears within the type `Unsharable`
note: required by `Middleware`

Вы можете решить проблему, изменив тип, чтобы сделать его совместимым с несколькими потоками:

use std::sync::Mutex;

struct Sharable(Mutex<Unsharable>);

impl Middleware for Sharable {}

Обратите внимание, что Rust сделал для вас очень хорошую вещь: он не позволил вам использовать тип, который небезопасен для вызова в нескольких потоках.


При написании своих тестов я хотел бы иметь возможность внедрить соединение в запрос, чтобы я мог заключить весь тестовый пример в транзакцию (даже если в тестовом примере имеется более одного запроса).

Я бы предположил, что возможно, архитектурные изменения будут еще лучше. Отделите домены "веб-фреймворка" от вашей "базы данных". Авторы " Растущего объектно-ориентированного программного обеспечения, руководствуясь тестами" (очень рекомендуемая книга) поддерживают этот стиль.

Разложите ваш код так, чтобы был метод, который просто принимает некоторый тип, который может начинать / заканчивать транзакцию, писать интересные вещи и тщательно их тестировать. Затем в веб-слое достаточно связующего кода для создания объекта транзакции, а затем вызовите следующий слой.

Другие вопросы по тегам