예제 #1
0
        /// <summary>
        /// Performs synchronization in an infinite loop. Periodically checks if source version has increased to trigger replication.
        /// </summary>
        /// <param name="token">The cancellation token.</param>
        public void SyncLoop(CancellationToken token)
        {
            var currentVersions = Enumerable.Repeat(0L, Config.ReplicationSets.Count).ToList();

            if (!Initialized)
            {
                Init();
            }

            while (true)
            {
                if (token.IsCancellationRequested)
                {
                    Log.Info("Stopping replication.");
                    return;
                }

                var start = DateTime.UtcNow;
                Error = false;

                for (int i = 0; i < Config.ReplicationSets.Count; i++)
                {
                    var  replicationSet = Config.ReplicationSets[i];
                    var  currentVersion = currentVersions[i];
                    long version        = 0;

                    try
                    {
                        using (var db = GetDatabase(replicationSet.Source.ConnectionString, DatabaseType.SqlServer2008))
                            version = db.ExecuteScalar <long>("select CHANGE_TRACKING_CURRENT_VERSION()");

                        Log.Debug($"Current version of source in replication set {replicationSet.Name} is {version}.");

                        if (version > currentVersion)
                        {
                            Log.Info($"Current version of source in replication set {replicationSet.Name} has increased from {currentVersion} to {version}: Starting replication.");

                            var tables  = Tables[i];
                            var success = Sync(replicationSet, tables, version);

                            if (success)
                            {
                                currentVersions[i] = version;
                            }

                            Synced?.Invoke(this, new SyncEventArgs {
                                ReplicationSet = replicationSet, Version = version
                            });
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, $"Error occurred during replication of set {replicationSet.Name}.");
                        Error = true;
                    }

                    if (token.IsCancellationRequested)
                    {
                        Log.Info("Stopping replication.");
                        return;
                    }
                }

                Log.Info($"Finished replication {(Error ? "with" : "without")} errors");

                var delay = (int)Math.Round(Math.Max(0, (TimeSpan.FromSeconds(Interval) - (DateTime.UtcNow - start)).TotalSeconds) * 1000, MidpointRounding.AwayFromZero);
                Thread.Sleep(delay);
            }
        }
예제 #2
0
 internal virtual void OnSynced() => Synced?.Invoke(this, EventArgs.Empty);
예제 #3
0
 private static void OnSynced() => Synced?.Invoke(null, EventArgs.Empty);
        private async Task PeerSyncAsync(CancellationToken token, PeerInfo peerInfo)
        {
            bool wasCancelled = false;

            ISynchronizationPeer peer       = peerInfo.Peer;
            BigInteger           bestNumber = BlockTree.BestSuggested.Number;

            const int maxLookup             = 64;
            int       ancestorLookupLevel   = 0;
            bool      isCommonAncestorKnown = false;

            while (peerInfo.NumberAvailable > bestNumber && peerInfo.NumberReceived <= peerInfo.NumberAvailable)
            {
                if (ancestorLookupLevel > maxLookup)
                {
                    throw new InvalidOperationException("Cannot find ancestor"); // TODO: remodel this after full sync test is added
                }

                if (token.IsCancellationRequested)
                {
                    token.ThrowIfCancellationRequested();
                }

                if (!isCommonAncestorKnown)
                {
                    // TODO: cases when many peers used for sync and one peer finished sync and then we need resync - we should start from common point and not NumberReceived that may be far in the past
                    _logger.Info($"Finding common ancestor for {peerInfo.Peer.NodeId}");
                    isCommonAncestorKnown = true;
                }

                BigInteger blocksLeft = peerInfo.NumberAvailable - peerInfo.NumberReceived;
                // TODO: fault handling on tasks

                int blocksToRequest = (int)BigInteger.Min(blocksLeft + 1, BatchSize);
                if (_logger.IsDebugEnabled)
                {
                    _logger.Debug($"Sync request to peer with {peerInfo.NumberAvailable} blocks. Got {peerInfo.NumberReceived} and asking for {blocksToRequest} more.");
                }
                Task <BlockHeader[]> headersTask = peer.GetBlockHeaders(peerInfo.NumberReceived, blocksToRequest, 0, token);
                _currentSyncTask = headersTask;
                BlockHeader[] headers = await headersTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCancelled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    _logger.Error("Failed to retrieve headers when synchronizing", _currentSyncTask.Exception);
                    throw _currentSyncTask.Exception;
                }

                if (token.IsCancellationRequested)
                {
                    token.ThrowIfCancellationRequested();
                }

                List <Keccak> hashes = new List <Keccak>();
                Dictionary <Keccak, BlockHeader> headersByHash = new Dictionary <Keccak, BlockHeader>();
                for (int i = 1; i < headers.Length; i++)
                {
                    hashes.Add(headers[i].Hash);
                    headersByHash[headers[i].Hash] = headers[i];
                }

                Task <Block[]> bodiesTask = peer.GetBlocks(hashes.ToArray(), token);
                _currentSyncTask = bodiesTask;
                Block[] blocks = await bodiesTask;
                if (_currentSyncTask.IsCanceled)
                {
                    wasCancelled = true;
                    break;
                }

                if (_currentSyncTask.IsFaulted)
                {
                    _logger.Error("Failed to retrieve bodies when synchronizing", _currentSyncTask.Exception);
                    throw _currentSyncTask.Exception;
                }

                ancestorLookupLevel = 0;

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    blocks[i].Header = headersByHash[hashes[i]];
                }

                if (blocks.Length > 0)
                {
                    Block parent = BlockTree.FindParent(blocks[0]);
                    if (parent == null)
                    {
                        ancestorLookupLevel     += BatchSize;
                        peerInfo.NumberReceived -= BatchSize;
                        continue;
                    }
                }

                // Parity 1.11 non canonical blocks when testing on 27/06
                for (int i = 0; i < blocks.Length; i++)
                {
                    if (i != 0 && blocks[i].ParentHash != blocks[i - 1].Hash)
                    {
                        throw new EthSynchronizationException("Peer send an inconsistent block list");
                    }
                }

                for (int i = 0; i < blocks.Length; i++)
                {
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Received {blocks[i]} from {peer.NodeId}");
                    }

                    if (_blockValidator.ValidateSuggestedBlock(blocks[i]))
                    {
                        AddBlockResult addResult = BlockTree.SuggestBlock(blocks[i]);
                        if (addResult == AddBlockResult.UnknownParent)
                        {
                            if (_logger.IsInfoEnabled)
                            {
                                _logger.Info($"Block {blocks[i].Number} ignored (unknown parent)");
                            }
                            if (i == 0)
                            {
                                if (_logger.IsWarnEnabled)
                                {
                                    _logger.Warn("Resyncing split");
                                }
                                peerInfo.NumberReceived -= 1;
                                var   syncTask = Task.Run(() => PeerSyncAsync(_syncCancellationTokenSource.Token, peerInfo), _syncCancellationTokenSource.Token);
                                await syncTask;
                            }
                            else
                            {
                                const string message = "Peer sent an inconsistent batch of block headers";
                                _logger.Error(message);
                                throw new EthSynchronizationException(message);
                            }
                        }

                        if (_logger.IsDebugEnabled)
                        {
                            _logger.Debug($"Block {blocks[i].Number} suggested for processing");
                        }
                    }
                    else
                    {
                        if (_logger.IsWarnEnabled)
                        {
                            _logger.Warn($"Block {blocks[i].Number} skipped (validation failed)");
                        }
                    }
                }

                peerInfo.NumberReceived = blocks[blocks.Length - 1].Number;

                bestNumber = BlockTree.BestSuggested.Number;
            }

            if (_logger.IsInfoEnabled)
            {
                _logger.Info($"Stopping sync processes with Node: {peerInfo.Peer.NodeId}, wasCancelled: {wasCancelled}");
            }

            if (!wasCancelled)
            {
                peerInfo.IsSynced = true;
                Synced?.Invoke(this, new SyncEventArgs(peerInfo.Peer));
            }
        }