22using System . Collections . Generic ;
33using System . Linq ;
44using System . Text ;
5+ using System . Threading . Tasks ;
6+ using Blockcore . Base ;
57using Blockcore . Configuration . Settings ;
68using Blockcore . Consensus ;
79using Blockcore . Features . Consensus . CoinViews . Coindb ;
@@ -130,7 +132,16 @@ public long GetScriptSize
130132
131133 private readonly Random random ;
132134
133- public CachedCoinView ( Network network , ICheckpoints checkpoints , ICoindb coindb , IDateTimeProvider dateTimeProvider , ILoggerFactory loggerFactory , INodeStats nodeStats , ConsensusSettings consensusSettings , StakeChainStore stakeChainStore = null , IRewindDataIndexCache rewindDataIndexCache = null )
135+ public CachedCoinView (
136+ Network network ,
137+ ICheckpoints checkpoints ,
138+ ICoindb coindb ,
139+ IDateTimeProvider dateTimeProvider ,
140+ ILoggerFactory loggerFactory ,
141+ INodeStats nodeStats ,
142+ ConsensusSettings consensusSettings ,
143+ StakeChainStore stakeChainStore = null ,
144+ IRewindDataIndexCache rewindDataIndexCache = null )
134145 {
135146 Guard . NotNull ( coindb , nameof ( CachedCoinView . coindb ) ) ;
136147
@@ -303,37 +314,43 @@ private void TryEvictCacheLocked()
303314 }
304315 }
305316
317+ /// <summary>
318+ /// Check if periodic flush is required.
319+ /// The conditions to flash the cache are if <see cref="CacheFlushTimeIntervalSeconds"/> is elapsed
320+ /// or if <see cref="MaxCacheSizeBytes"/> is reached.
321+ /// </summary>
322+ /// <returns>True if the coinview needs to flush</returns>
323+ public bool ShouldFlush ( )
324+ {
325+ DateTime now = this . dateTimeProvider . GetUtcNow ( ) ;
326+ bool flushTimeLimit = ( now - this . lastCacheFlushTime ) . TotalSeconds >= this . CacheFlushTimeIntervalSeconds ;
327+
328+ // The size of the cache was reached and most likely TryEvictCacheLocked didn't work
329+ // so the cache is polluted with flushable items, then we flush anyway.
330+
331+ long totalBytes = this . cacheSizeBytes + this . rewindDataSizeBytes ;
332+ bool flushSizeLimit = totalBytes > this . MaxCacheSizeBytes ;
333+
334+ if ( ! flushTimeLimit && ! flushSizeLimit )
335+ {
336+ return false ;
337+ }
338+
339+ this . logger . LogDebug ( "Flushing, reasons flushTimeLimit={0} flushSizeLimit={1}." , flushTimeLimit , flushSizeLimit ) ;
340+
341+ return true ;
342+ }
343+
306344 /// <summary>
307345 /// Finds all changed records in the cache and persists them to the underlying coinview.
308346 /// </summary>
309347 /// <param name="force"><c>true</c> to enforce flush, <c>false</c> to flush only if <see cref="lastCacheFlushTime"/> is older than <see cref="CacheFlushTimeIntervalSeconds"/>.</param>
310- /// <remarks>
311- /// WARNING: This method can only be run from <see cref="ConsensusLoop.Execute(System.Threading.CancellationToken)"/> thread context
312- /// or when consensus loop is stopped. Otherwise, there is a risk of race condition when the consensus loop accepts new block.
313- /// </remarks>
314348 public void Flush ( bool force = true )
315349 {
316350 if ( ! force )
317351 {
318- // Check if periodic flush is reuired.
319- // Ideally this will flush less frequent and always be behind
320- // blockstore which is currently set to 17 sec.
321-
322- DateTime now = this . dateTimeProvider . GetUtcNow ( ) ;
323- bool flushTimeLimit = ( now - this . lastCacheFlushTime ) . TotalSeconds >= this . CacheFlushTimeIntervalSeconds ;
324-
325- // The size of the cache was reached and most likely TryEvictCacheLocked didn't work
326- // so the cahces is pulledted with flushable items, then we flush anyway.
327-
328- long totalBytes = this . cacheSizeBytes + this . rewindDataSizeBytes ;
329- bool flushSizeLimit = totalBytes > this . MaxCacheSizeBytes ;
330-
331- if ( ! flushTimeLimit && ! flushSizeLimit )
332- {
352+ if ( ! this . ShouldFlush ( ) )
333353 return ;
334- }
335-
336- this . logger . LogDebug ( "Flushing, reasons flushTimeLimit={0} flushSizeLimit={1}." , flushTimeLimit , flushSizeLimit ) ;
337354 }
338355
339356 // Before flushing the coinview persist the stake store
@@ -408,7 +425,7 @@ public void SaveChanges(IList<UnspentOutput> outputs, HashHeightPair oldBlockHas
408425 if ( ! this . cachedUtxoItems . TryGetValue ( output . OutPoint , out CacheItem cacheItem ) )
409426 {
410427 // Add outputs to cache, this will happen for two cases
411- // 1. if a chaced item was evicted
428+ // 1. if a cached item was evicted
412429 // 2. for new outputs that are added
413430
414431 if ( output . CreatedFromBlock )
@@ -428,8 +445,8 @@ public void SaveChanges(IList<UnspentOutput> outputs, HashHeightPair oldBlockHas
428445 }
429446 else
430447 {
431- // This can happen if the cashe item was evicted while
432- // the block was being processed, fetch the outut again from disk.
448+ // This can happen if the cached item was evicted while
449+ // the block was being processed, fetch the output again from disk.
433450
434451 this . logger . LogDebug ( "Outpoint '{0}' is not found in cache, creating it." , output . OutPoint ) ;
435452
@@ -521,12 +538,12 @@ public void SaveChanges(IList<UnspentOutput> outputs, HashHeightPair oldBlockHas
521538
522539 this . logger . LogDebug ( "Coin override alllowed for utxo '{0}'." , cacheItem . OutPoint ) ;
523540
524- // Deduct the crurrent script size form the
541+ // Deduct the current script size form the
525542 // total cache size, it will be added again later.
526543 this . cacheSizeBytes -= cacheItem . GetScriptSize ;
527544
528- // Clear this in order to calculate the cache sie
529- // this will get set later when overriden
545+ // Clear this in order to calculate the cache size
546+ // this will get set later when overridden
530547 cacheItem . Coins = null ;
531548 }
532549
@@ -573,27 +590,11 @@ public void SaveChanges(IList<UnspentOutput> outputs, HashHeightPair oldBlockHas
573590 // When cache is flushed the rewind data will allow to rewind the node up to the
574591 // number of rewind blocks.
575592 // TODO: move rewind data to use block store.
576- // Rewind data can go away all togetehr if the node uses the blocks in block store
593+ // Rewind data can go away all together if the node uses the blocks in block store
577594 // to get the rewind information, blockstore persists much more frequent then coin cache
578595 // So using block store for rewinds is not entirely impossible.
579596
580- uint rewindDataWindow = 10 ;
581-
582- if ( this . blockHash . Height >= this . lastCheckpointHeight )
583- {
584- if ( this . network . Consensus . MaxReorgLength != 0 )
585- {
586- rewindDataWindow = this . network . Consensus . MaxReorgLength + 1 ;
587- }
588- else
589- {
590- // TODO: make the rewind data window a configuration
591- // parameter of evern a network parameter.
592-
593- // For POW assume BTC where a rewind data of 100 is more then enough.
594- rewindDataWindow = 100 ;
595- }
596- }
597+ int rewindDataWindow = this . CalculateRewindWindow ( ) ;
597598
598599 int rewindToRemove = this . blockHash . Height - ( int ) rewindDataWindow ;
599600
@@ -606,6 +607,33 @@ public void SaveChanges(IList<UnspentOutput> outputs, HashHeightPair oldBlockHas
606607 }
607608 }
608609
610+ /// <summary>
611+ /// Calculate the window of how many rewind items to keep in memory.
612+ /// </summary>
613+ /// <returns></returns>
614+ public int CalculateRewindWindow ( )
615+ {
616+ uint rewindDataWindow = 10 ;
617+
618+ if ( this . blockHash . Height >= this . lastCheckpointHeight )
619+ {
620+ if ( this . network . Consensus . MaxReorgLength != 0 )
621+ {
622+ rewindDataWindow = this . network . Consensus . MaxReorgLength + 1 ;
623+ }
624+ else
625+ {
626+ // TODO: make the rewind data window a configuration
627+ // parameter of every a network parameter.
628+
629+ // For POW assume BTC where a rewind data of 100 is more then enough.
630+ rewindDataWindow = 100 ;
631+ }
632+ }
633+
634+ return ( int ) rewindDataWindow ;
635+ }
636+
609637 public HashHeightPair Rewind ( )
610638 {
611639 if ( this . innerBlockHash == null )
0 commit comments