@@ -24,6 +24,9 @@ use slog::{info, Logger};
24
24
pub trait UpkeepService : Send + Sync {
25
25
/// Run the upkeep service.
26
26
async fn run ( & self , epoch : Epoch ) -> StdResult < ( ) > ;
27
+
28
+ /// Vacuum database.
29
+ async fn vacuum ( & self ) -> StdResult < ( ) > ;
27
30
}
28
31
29
32
/// Define the task responsible for pruning a datasource below a certain epoch threshold.
@@ -101,10 +104,7 @@ impl AggregatorUpkeepService {
101
104
info ! ( db_upkeep_logger, "Cleaning main database" ) ;
102
105
SqliteCleaner :: new ( & main_db_connection)
103
106
. with_logger ( db_upkeep_logger. clone ( ) )
104
- . with_tasks ( & [
105
- SqliteCleaningTask :: Vacuum ,
106
- SqliteCleaningTask :: WalCheckpointTruncate ,
107
- ] )
107
+ . with_tasks ( & [ SqliteCleaningTask :: WalCheckpointTruncate ] )
108
108
. run ( ) ?;
109
109
110
110
info ! ( db_upkeep_logger, "Cleaning cardano transactions database" ) ;
@@ -127,6 +127,34 @@ impl AggregatorUpkeepService {
127
127
. await
128
128
. with_context ( || "Database Upkeep thread crashed" ) ?
129
129
}
130
+
131
+ async fn vacuum_main_database ( & self ) -> StdResult < ( ) > {
132
+ if self . signed_entity_type_lock . has_locked_entities ( ) . await {
133
+ info ! (
134
+ self . logger,
135
+ "Some entities are locked - Skipping main database vacuum"
136
+ ) ;
137
+ return Ok ( ( ) ) ;
138
+ }
139
+
140
+ let main_db_connection = self . main_db_connection . clone ( ) ;
141
+ let db_upkeep_logger = self . logger . clone ( ) ;
142
+
143
+ // Run the database upkeep tasks in another thread to avoid blocking the tokio runtime
144
+ let db_upkeep_thread = tokio:: task:: spawn_blocking ( move || -> StdResult < ( ) > {
145
+ info ! ( db_upkeep_logger, "Vacuum main database" ) ;
146
+ SqliteCleaner :: new ( & main_db_connection)
147
+ . with_logger ( db_upkeep_logger. clone ( ) )
148
+ . with_tasks ( & [ SqliteCleaningTask :: Vacuum ] )
149
+ . run ( ) ?;
150
+
151
+ Ok ( ( ) )
152
+ } ) ;
153
+
154
+ db_upkeep_thread
155
+ . await
156
+ . with_context ( || "Database Upkeep thread crashed" ) ?
157
+ }
130
158
}
131
159
132
160
#[ async_trait]
@@ -145,12 +173,23 @@ impl UpkeepService for AggregatorUpkeepService {
145
173
info ! ( self . logger, "Upkeep finished" ) ;
146
174
Ok ( ( ) )
147
175
}
176
+
177
+ async fn vacuum ( & self ) -> StdResult < ( ) > {
178
+ info ! ( self . logger, "Start database vacuum" ) ;
179
+
180
+ self . vacuum_main_database ( )
181
+ . await
182
+ . with_context ( || "Vacuuming main database failed" ) ?;
183
+
184
+ info ! ( self . logger, "Vacuum finished" ) ;
185
+
186
+ Ok ( ( ) )
187
+ }
148
188
}
149
189
150
190
#[ cfg( test) ]
151
191
mod tests {
152
- use mithril_common:: entities:: SignedEntityTypeDiscriminants ;
153
- use mithril_common:: test_utils:: TempDir ;
192
+ use mithril_common:: { entities:: SignedEntityTypeDiscriminants , temp_dir_create} ;
154
193
use mockall:: predicate:: eq;
155
194
156
195
use crate :: database:: test_helper:: {
@@ -187,7 +226,7 @@ mod tests {
187
226
#[ tokio:: test]
188
227
async fn test_cleanup_database ( ) {
189
228
let ( main_db_path, ctx_db_path, event_store_db_path, log_path) = {
190
- let db_dir = TempDir :: create ( "aggregator_upkeep" , "test_cleanup_database" ) ;
229
+ let db_dir = temp_dir_create ! ( ) ;
191
230
(
192
231
db_dir. join ( "main.db" ) ,
193
232
db_dir. join ( "cardano_tx.db" ) ,
@@ -218,27 +257,23 @@ mod tests {
218
257
219
258
let logs = std:: fs:: read_to_string ( & log_path) . unwrap ( ) ;
220
259
221
- assert_eq ! (
222
- logs. matches( SqliteCleaningTask :: Vacuum . log_message( ) )
223
- . count( ) ,
224
- 1 ,
225
- "Should have run only once since only the main database has a `Vacuum` cleanup"
226
- ) ;
227
260
assert_eq ! (
228
261
logs. matches( SqliteCleaningTask :: WalCheckpointTruncate . log_message( ) )
229
262
. count( ) ,
230
263
3 ,
231
264
"Should have run three times since the three databases have a `WalCheckpointTruncate` cleanup"
232
265
) ;
266
+ assert_eq ! (
267
+ logs. matches( SqliteCleaningTask :: Vacuum . log_message( ) )
268
+ . count( ) ,
269
+ 0 ,
270
+ "Upkeep operation should not include Vacuum tasks"
271
+ ) ;
233
272
}
234
273
235
274
#[ tokio:: test]
236
275
async fn test_doesnt_cleanup_db_if_any_entity_is_locked ( ) {
237
- let log_path = TempDir :: create (
238
- "aggregator_upkeep" ,
239
- "test_doesnt_cleanup_db_if_any_entity_is_locked" ,
240
- )
241
- . join ( "upkeep.log" ) ;
276
+ let log_path = temp_dir_create ! ( ) . join ( "upkeep.log" ) ;
242
277
243
278
let signed_entity_type_lock = Arc :: new ( SignedEntityTypeLock :: default ( ) ) ;
244
279
signed_entity_type_lock
@@ -257,11 +292,6 @@ mod tests {
257
292
258
293
let logs = std:: fs:: read_to_string ( & log_path) . unwrap ( ) ;
259
294
260
- assert_eq ! (
261
- logs. matches( SqliteCleaningTask :: Vacuum . log_message( ) )
262
- . count( ) ,
263
- 0 ,
264
- ) ;
265
295
assert_eq ! (
266
296
logs. matches( SqliteCleaningTask :: WalCheckpointTruncate . log_message( ) )
267
297
. count( ) ,
@@ -290,4 +320,59 @@ mod tests {
290
320
291
321
service. run ( Epoch ( 14 ) ) . await . expect ( "Upkeep service failed" ) ;
292
322
}
323
+
324
+ #[ tokio:: test]
325
+ async fn test_doesnt_vacuum_db_if_any_entity_is_locked ( ) {
326
+ let log_path = temp_dir_create ! ( ) . join ( "vacuum.log" ) ;
327
+
328
+ let signed_entity_type_lock = Arc :: new ( SignedEntityTypeLock :: default ( ) ) ;
329
+ signed_entity_type_lock
330
+ . lock ( SignedEntityTypeDiscriminants :: CardanoTransactions )
331
+ . await ;
332
+
333
+ // Separate block to force log flushing by dropping the service that owns the logger
334
+ {
335
+ let service = AggregatorUpkeepService {
336
+ signed_entity_type_lock : signed_entity_type_lock. clone ( ) ,
337
+ logger : TestLogger :: file ( & log_path) ,
338
+ ..default_upkeep_service ( )
339
+ } ;
340
+ service. vacuum ( ) . await . expect ( "Vacuum failed" ) ;
341
+ }
342
+
343
+ let logs = std:: fs:: read_to_string ( & log_path) . unwrap ( ) ;
344
+
345
+ assert_eq ! (
346
+ logs. matches( SqliteCleaningTask :: Vacuum . log_message( ) )
347
+ . count( ) ,
348
+ 0 ,
349
+ ) ;
350
+ }
351
+
352
+ #[ tokio:: test]
353
+ async fn test_vacuum_database ( ) {
354
+ let log_path = temp_dir_create ! ( ) . join ( "vacuum.log" ) ;
355
+
356
+ let signed_entity_type_lock = Arc :: new ( SignedEntityTypeLock :: default ( ) ) ;
357
+ signed_entity_type_lock
358
+ . lock ( SignedEntityTypeDiscriminants :: CardanoTransactions )
359
+ . await ;
360
+
361
+ // Separate block to force log flushing by dropping the service that owns the logger
362
+ {
363
+ let service = AggregatorUpkeepService {
364
+ logger : TestLogger :: file ( & log_path) ,
365
+ ..default_upkeep_service ( )
366
+ } ;
367
+ service. vacuum ( ) . await . expect ( "Vacuum failed" ) ;
368
+ }
369
+
370
+ let logs = std:: fs:: read_to_string ( & log_path) . unwrap ( ) ;
371
+
372
+ assert_eq ! (
373
+ logs. matches( SqliteCleaningTask :: Vacuum . log_message( ) )
374
+ . count( ) ,
375
+ 1 ,
376
+ ) ;
377
+ }
293
378
}
0 commit comments