Внедрение дизельного соединения в промежуточное программное обеспечение 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 сделал для вас очень хорошую вещь: он не позволил вам использовать тип, который небезопасен для вызова в нескольких потоках.
При написании своих тестов я хотел бы иметь возможность внедрить соединение в запрос, чтобы я мог заключить весь тестовый пример в транзакцию (даже если в тестовом примере имеется более одного запроса).
Я бы предположил, что возможно, архитектурные изменения будут еще лучше. Отделите домены "веб-фреймворка" от вашей "базы данных". Авторы " Растущего объектно-ориентированного программного обеспечения, руководствуясь тестами" (очень рекомендуемая книга) поддерживают этот стиль.
Разложите ваш код так, чтобы был метод, который просто принимает некоторый тип, который может начинать / заканчивать транзакцию, писать интересные вещи и тщательно их тестировать. Затем в веб-слое достаточно связующего кода для создания объекта транзакции, а затем вызовите следующий слой.