private IEnumerable <FilterLog> FilterLogsWithBloomsIndex(LogFilter filter, BlockHeader fromBlock, BlockHeader toBlock) { Keccak FindBlockHash(long blockNumber) { var blockHash = _blockFinder.FindBlockHash(blockNumber); if (blockHash == null) { if (_logger.IsError) { _logger.Error($"Could not find block {blockNumber} in database. eth_getLogs will return incomplete results."); } } return(blockHash); } IEnumerable <long> FilterBlocks(LogFilter f, long from, long to) { var enumeration = _bloomStorage.GetBlooms(from, to); foreach (var bloom in enumeration) { if (f.Matches(bloom) && enumeration.TryGetBlockNumber(out var blockNumber)) { yield return(blockNumber); } } } return(FilterBlocks(filter, fromBlock.Number, toBlock.Number) .AsParallel() // can yield big performance improvements .AsOrdered() // we want to keep block order .SelectMany(blockNumber => FindLogsInBlock(filter, FindBlockHash(blockNumber), blockNumber))); }
private IEnumerable <FilterLog> FilterLogsWithBloomsIndex(LogFilter filter, BlockHeader fromBlock, BlockHeader toBlock, CancellationToken cancellationToken) { Keccak FindBlockHash(long blockNumber, CancellationToken token) { token.ThrowIfCancellationRequested(); var blockHash = _blockFinder.FindBlockHash(blockNumber); if (blockHash == null) { if (_logger.IsError) { _logger.Error($"Could not find block {blockNumber} in database. eth_getLogs will return incomplete results."); } } return(blockHash); } IEnumerable <long> FilterBlocks(LogFilter f, long @from, long to, bool runParallel, CancellationToken token) { try { var enumeration = _bloomStorage.GetBlooms(from, to); foreach (var bloom in enumeration) { token.ThrowIfCancellationRequested(); if (f.Matches(bloom) && enumeration.TryGetBlockNumber(out var blockNumber)) { yield return(blockNumber); } } } finally { if (runParallel) { Interlocked.CompareExchange(ref ParallelLock, 0, 1); } Interlocked.Decrement(ref ParallelExecutions); } } // we want to support one parallel eth_getLogs call for maximum performance // we don't want support more than one eth_getLogs call so we don't starve CPU and threads int parallelLock = Interlocked.CompareExchange(ref ParallelLock, 1, 0); int parallelExecutions = Interlocked.Increment(ref ParallelExecutions) - 1; bool canRunParallel = parallelLock == 0; IEnumerable <long> filterBlocks = FilterBlocks(filter, fromBlock.Number, toBlock.Number, canRunParallel, cancellationToken); if (canRunParallel) { if (_logger.IsTrace) { _logger.Trace($"Allowing parallel eth_getLogs, already parallel executions: {parallelExecutions}."); } filterBlocks = filterBlocks.AsParallel() // can yield big performance improvements .AsOrdered() // we want to keep block order .WithDegreeOfParallelism(_rpcConfigGetLogsThreads); // explicitly provide number of threads } else { if (_logger.IsTrace) { _logger.Trace($"Not allowing parallel eth_getLogs, already parallel executions: {parallelExecutions}."); } } return(filterBlocks .SelectMany(blockNumber => FindLogsInBlock(filter, FindBlockHash(blockNumber, cancellationToken), blockNumber, cancellationToken))); }