@@ -30,34 +30,53 @@ use crate::storage::storage_api::{
30
30
31
31
use super :: { queries:: QueryFromConnection , TransactionalApiServerPostgresStorage } ;
32
32
33
+ const CONN_ERR : & str = "CRITICAL ERROR: failed to get postgres tx connection. Invariant broken." ;
34
+
33
35
pub struct ApiServerPostgresTransactionalRo < ' a > {
34
- connection : PooledConnection < ' a , PostgresConnectionManager < NoTls > > ,
36
+ // Note: This is an Option due to needing to pry the connection out of Self in Drop
37
+ connection : Option < PooledConnection < ' static , PostgresConnectionManager < NoTls > > > ,
35
38
finished : bool ,
39
+ db_tx_sender : tokio:: sync:: mpsc:: UnboundedSender <
40
+ PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
41
+ > ,
42
+ // Note: This exists to enforce that a transaction never outlives the database object,
43
+ // given that all connections have 'static lifetimes
44
+ _marker : std:: marker:: PhantomData < & ' a ( ) > ,
36
45
}
37
46
38
47
impl < ' a > ApiServerPostgresTransactionalRo < ' a > {
39
48
pub ( super ) async fn from_connection (
40
- connection : PooledConnection < ' a , PostgresConnectionManager < NoTls > > ,
41
- ) -> Result < ApiServerPostgresTransactionalRo , ApiServerStorageError > {
49
+ connection : PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
50
+ db_tx_sender : tokio:: sync:: mpsc:: UnboundedSender <
51
+ PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
52
+ > ,
53
+ ) -> Result < ApiServerPostgresTransactionalRo < ' a > , ApiServerStorageError > {
42
54
let tx = Self {
43
- connection,
55
+ connection : Some ( connection ) ,
44
56
finished : false ,
57
+ db_tx_sender,
58
+ _marker : std:: marker:: PhantomData ,
45
59
} ;
46
- tx. connection . batch_execute ( "BEGIN READ ONLY" ) . await . map_err ( |e| {
47
- ApiServerStorageError :: RoTxBeginFailed ( format ! ( "Transaction begin failed: {}" , e) )
48
- } ) ?;
60
+ tx. connection
61
+ . as_ref ( )
62
+ . expect ( CONN_ERR )
63
+ . batch_execute ( "BEGIN READ ONLY" )
64
+ . await
65
+ . map_err ( |e| {
66
+ ApiServerStorageError :: RoTxBeginFailed ( format ! ( "Transaction begin failed: {}" , e) )
67
+ } ) ?;
49
68
Ok ( tx)
50
69
}
51
70
52
71
pub async fn is_initialized ( & mut self ) -> Result < bool , ApiServerStorageError > {
53
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
72
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
54
73
let res = conn. is_initialized ( ) . await ?;
55
74
56
75
Ok ( res)
57
76
}
58
77
59
78
pub async fn get_storage_version ( & mut self ) -> Result < Option < u32 > , ApiServerStorageError > {
60
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
79
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
61
80
let res = conn. get_storage_version ( ) . await ?;
62
81
63
82
Ok ( res)
@@ -66,7 +85,7 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
66
85
pub async fn get_best_block (
67
86
& mut self ,
68
87
) -> Result < ( BlockHeight , Id < GenBlock > ) , ApiServerStorageError > {
69
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
88
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
70
89
let res = conn. get_best_block ( ) . await ?;
71
90
72
91
Ok ( res)
@@ -76,7 +95,7 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
76
95
& mut self ,
77
96
block_height : BlockHeight ,
78
97
) -> Result < Option < Id < Block > > , ApiServerStorageError > {
79
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
98
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
80
99
let res = conn. get_main_chain_block_id ( block_height) . await ?;
81
100
82
101
Ok ( res)
@@ -86,7 +105,7 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
86
105
& mut self ,
87
106
block_id : Id < Block > ,
88
107
) -> Result < Option < Block > , ApiServerStorageError > {
89
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
108
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
90
109
let res = conn. get_block ( block_id) . await ?;
91
110
92
111
Ok ( res)
@@ -97,7 +116,7 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
97
116
& mut self ,
98
117
transaction_id : Id < Transaction > ,
99
118
) -> Result < Option < ( Option < Id < Block > > , SignedTransaction ) > , ApiServerStorageError > {
100
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
119
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
101
120
let res = conn. get_transaction ( transaction_id) . await ?;
102
121
103
122
Ok ( res)
@@ -107,43 +126,60 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
107
126
& mut self ,
108
127
block_id : Id < Block > ,
109
128
) -> Result < Option < BlockAuxData > , ApiServerStorageError > {
110
- let mut conn = QueryFromConnection :: new ( & self . connection ) ;
129
+ let mut conn = QueryFromConnection :: new ( self . connection . as_ref ( ) . expect ( CONN_ERR ) ) ;
111
130
let res = conn. get_block_aux_data ( block_id) . await ?;
112
131
113
132
Ok ( res)
114
133
}
115
134
}
116
135
117
136
pub struct ApiServerPostgresTransactionalRw < ' a > {
118
- connection : PooledConnection < ' a , PostgresConnectionManager < NoTls > > ,
137
+ // Note: This is an Option due to needing to pry the connection out of Self in Drop
138
+ connection : Option < PooledConnection < ' static , PostgresConnectionManager < NoTls > > > ,
119
139
finished : bool ,
140
+ db_tx_sender : tokio:: sync:: mpsc:: UnboundedSender <
141
+ PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
142
+ > ,
143
+ // Note: This exists to enforce that a transaction never outlives the database object,
144
+ // given that all connections have 'static lifetimes
145
+ _marker : std:: marker:: PhantomData < & ' a ( ) > ,
120
146
}
121
147
122
148
impl < ' a > Drop for ApiServerPostgresTransactionalRw < ' a > {
123
149
fn drop ( & mut self ) {
124
150
if !self . finished {
125
- futures:: executor:: block_on ( self . connection . batch_execute ( "ROLLBACK" ) ) . unwrap_or_else (
126
- |e| {
151
+ self . db_tx_sender
152
+ . send ( self . connection . take ( ) . expect ( CONN_ERR ) )
153
+ . unwrap_or_else ( |e| {
127
154
logging:: log:: error!(
128
- "CRITICAL ERROR: failed to rollback failed postgres RW transaction: {e}"
155
+ "CRITICAL ERROR: failed to send postgres RW transaction connection for closure : {e}"
129
156
)
130
- } ,
131
- ) ;
157
+ } ) ;
132
158
}
133
159
}
134
160
}
135
161
136
162
impl < ' a > ApiServerPostgresTransactionalRw < ' a > {
137
163
pub ( super ) async fn from_connection (
138
- connection : PooledConnection < ' a , PostgresConnectionManager < NoTls > > ,
164
+ connection : PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
165
+ db_tx_sender : tokio:: sync:: mpsc:: UnboundedSender <
166
+ PooledConnection < ' static , PostgresConnectionManager < NoTls > > ,
167
+ > ,
139
168
) -> Result < ApiServerPostgresTransactionalRw < ' a > , ApiServerStorageError > {
140
169
let tx = Self {
141
- connection,
170
+ connection : Some ( connection ) ,
142
171
finished : false ,
172
+ db_tx_sender,
173
+ _marker : std:: marker:: PhantomData ,
143
174
} ;
144
- tx. connection . batch_execute ( "BEGIN READ WRITE" ) . await . map_err ( |e| {
145
- ApiServerStorageError :: RwTxBeginFailed ( format ! ( "Transaction begin failed: {}" , e) )
146
- } ) ?;
175
+ tx. connection
176
+ . as_ref ( )
177
+ . expect ( CONN_ERR )
178
+ . batch_execute ( "BEGIN READ WRITE" )
179
+ . await
180
+ . map_err ( |e| {
181
+ ApiServerStorageError :: RwTxBeginFailed ( format ! ( "Transaction begin failed: {}" , e) )
182
+ } ) ?;
147
183
Ok ( tx)
148
184
}
149
185
}
@@ -152,6 +188,8 @@ impl<'a> ApiServerPostgresTransactionalRw<'a> {
152
188
impl < ' a > ApiServerTransactionRw for ApiServerPostgresTransactionalRw < ' a > {
153
189
async fn commit ( mut self ) -> Result < ( ) , crate :: storage:: storage_api:: ApiServerStorageError > {
154
190
self . connection
191
+ . as_ref ( )
192
+ . expect ( CONN_ERR )
155
193
. batch_execute ( "COMMIT" )
156
194
. await
157
195
. map_err ( |e| ApiServerStorageError :: TxCommitFailed ( e. to_string ( ) ) ) ?;
@@ -161,6 +199,8 @@ impl<'a> ApiServerTransactionRw for ApiServerPostgresTransactionalRw<'a> {
161
199
162
200
async fn rollback ( mut self ) -> Result < ( ) , crate :: storage:: storage_api:: ApiServerStorageError > {
163
201
self . connection
202
+ . as_ref ( )
203
+ . expect ( CONN_ERR )
164
204
. batch_execute ( "ROLLBACK" )
165
205
. await
166
206
. map_err ( |e| ApiServerStorageError :: TxCommitFailed ( e. to_string ( ) ) ) ?;
@@ -179,31 +219,31 @@ impl<'a> ApiServerTransactionRo for ApiServerPostgresTransactionalRo<'a> {
179
219
impl < ' a > Drop for ApiServerPostgresTransactionalRo < ' a > {
180
220
fn drop ( & mut self ) {
181
221
if !self . finished {
182
- futures:: executor:: block_on ( self . connection . batch_execute ( "ROLLBACK" ) ) . unwrap_or_else (
183
- |e| {
222
+ self . db_tx_sender
223
+ . send ( self . connection . take ( ) . expect ( CONN_ERR ) )
224
+ . unwrap_or_else ( |e| {
184
225
logging:: log:: error!(
185
- "CRITICAL ERROR: failed to rollback failed postgres RO transaction: {e}"
226
+ "CRITICAL ERROR: failed to send postgres RO transaction connection for closure : {e}"
186
227
)
187
- } ,
188
- ) ;
228
+ } ) ;
189
229
}
190
230
}
191
231
}
192
232
193
233
#[ async_trait:: async_trait]
194
- impl < ' t > Transactional < ' t > for TransactionalApiServerPostgresStorage {
195
- type TransactionRo = ApiServerPostgresTransactionalRo < ' t > ;
234
+ impl < ' tx > Transactional < ' tx > for TransactionalApiServerPostgresStorage {
235
+ type TransactionRo = ApiServerPostgresTransactionalRo < ' tx > ;
196
236
197
- type TransactionRw = ApiServerPostgresTransactionalRw < ' t > ;
237
+ type TransactionRw = ApiServerPostgresTransactionalRw < ' tx > ;
198
238
199
- async fn transaction_ro < ' s : ' t > (
200
- & ' s self ,
239
+ async fn transaction_ro < ' db : ' tx > (
240
+ & ' db self ,
201
241
) -> Result < Self :: TransactionRo , ApiServerStorageError > {
202
242
self . begin_ro_transaction ( ) . await
203
243
}
204
244
205
- async fn transaction_rw < ' s : ' t > (
206
- & ' s mut self ,
245
+ async fn transaction_rw < ' db : ' tx > (
246
+ & ' db mut self ,
207
247
) -> Result < Self :: TransactionRw , ApiServerStorageError > {
208
248
self . begin_rw_transaction ( ) . await
209
249
}
0 commit comments