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)));
        }
Exemple #2
0
        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)));
        }