@@ -306,6 +306,10 @@ pub struct Client {
306306
307307 shutdown : Arc < ShutdownManager > ,
308308
309+ /// block number and block hash of latest gc.
310+ /// this information is used to avoid double garbage collection.
311+ garbage_collect_latest_block : Mutex < ( u64 , H256 ) > ,
312+
309313 statistics : ClientStatistics ,
310314}
311315
@@ -842,6 +846,8 @@ impl Importer {
842846 warn ! ( "Failed to prune ancient state data: {}" , e) ;
843847 }
844848
849+ client. schedule_garbage_collect_in_queue ( ) ;
850+
845851 route
846852 }
847853
@@ -1107,6 +1113,7 @@ impl Client {
11071113 importer,
11081114 config,
11091115 shutdown,
1116+ garbage_collect_latest_block : Mutex :: new ( ( 0 , H256 :: zero ( ) ) ) ,
11101117 statistics,
11111118 } ) ;
11121119
@@ -1540,6 +1547,52 @@ impl Client {
15401547 }
15411548 }
15421549
1550+ /// Schedule garbage collection of invalid service transactions from the transaction queue based on the given block hash.
1551+ pub fn schedule_garbage_collect_in_queue ( & self ) {
1552+ let m = ClientIoMessage :: execute ( |c| c. garbage_collect_in_queue ( ) ) ;
1553+ if let Err ( e) = self . io_channel . read ( ) . send ( m) {
1554+ error ! ( target: "client" , "Failed to schedule garbage collection in transaction queue for block {:?}" , e) ;
1555+ }
1556+ }
1557+
1558+ /// Garbage collect invalid service transactions from the transaction queue based on the given block header.
1559+ pub fn garbage_collect_in_queue ( & self ) {
1560+ let machine = self . engine ( ) . machine ( ) ;
1561+
1562+ match & self . block_header_decoded ( BlockId :: Latest ) {
1563+ Some ( block_header) => {
1564+ {
1565+ // scope for mutex.
1566+ let mut last_gc = self . garbage_collect_latest_block . lock ( ) ;
1567+
1568+ if block_header. number ( ) == last_gc. 0 && block_header. hash ( ) == last_gc. 1 {
1569+ // already gced for this block, or gc is ongoing.
1570+ // we can return here.
1571+ return ;
1572+ }
1573+
1574+ // we treat ongoing gc as DONE, to avoid blocking of the message channel
1575+ last_gc. 0 = block_header. number ( ) ;
1576+ last_gc. 1 = block_header. hash ( ) ;
1577+ }
1578+
1579+ // here hides an accepted race condition.
1580+ // latest block could change during long ongoing GCs.
1581+ // this could be avoided developing a more complex GC logic.
1582+ // but the GC blocks the tx queue, so it has to be blazing fast.
1583+ self . importer . miner . collect_garbage ( |tx|
1584+ match machine. verify_transaction ( tx. signed ( ) , block_header, self ) {
1585+ Ok ( _) => true ,
1586+ Err ( e) => {
1587+ trace ! ( target: "client" , "collected garbage transaction from {:?}: {:?} reason: {:?}" , tx. signed( ) . sender( ) , tx. signed( ) . hash, e) ;
1588+ false
1589+ } ,
1590+ } ) ;
1591+ }
1592+ None => { }
1593+ }
1594+ }
1595+
15431596 fn check_garbage ( & self ) {
15441597 self . chain . read ( ) . collect_garbage ( ) ;
15451598 self . importer . block_queue . collect_garbage ( ) ;
0 commit comments