private void IncomingHandlerRemoved(IncomingReplicationHandler handler)
 {
     if (_incoming.TryRemove(handler.ConnectionInfo.SourceDatabaseId, out var stats))
     {
         stats.Handler.DocumentsReceived -= IncomingDocumentsReceived;
     }
 }
示例#2
0
        public void All_local_etags_lower_than_remote_should_return_Update_at_conflict_status()
        {
            var dbIds = new List <Guid> {
                Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()
            };

            var local = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 1
                },
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 2
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 3
                },
            };

            var remote = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 10
                },
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 20
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 30
                },
            };

            Assert.Equal(IncomingReplicationHandler.ConflictStatus.Update, IncomingReplicationHandler.GetConflictStatus(remote, local));
        }
示例#3
0
        public void Some_remote_etags_lower_than_local_and_some_higher_should_return_Conflict_at_conflict_status_with_different_order()
        {
            var dbIds = new List <Guid> {
                Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()
            };

            var local = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 75
                },
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 10
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 3
                },
            };

            var remote = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 95
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 2
                },
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 10
                },
            };

            Assert.Equal(IncomingReplicationHandler.ConflictStatus.Conflict, IncomingReplicationHandler.GetConflictStatus(remote, local));
        }
        private void IncomingHandlerAdded(IncomingReplicationHandler handler)
        {
            _incoming.GetOrAdd(handler.ConnectionInfo.SourceDatabaseId, key =>
            {
                handler.DocumentsReceived += IncomingDocumentsReceived;

                return(new ReplicationHandlerAndPerformanceStatsList <IncomingReplicationHandler, IncomingReplicationStatsAggregator>(handler));
            });
        }
示例#5
0
 private void OnIncomingReceiveSucceeded(IncomingReplicationHandler instance)
 {
     _incomingLastActivityTime.AddOrUpdate(instance.ConnectionInfo, DateTime.UtcNow, (_, __) => DateTime.UtcNow);
     foreach (var handler in _incoming.Values)
     {
         if (handler != instance)
         {
             handler.OnReplicationFromAnotherSource();
         }
     }
 }
示例#6
0
        private void OnIncomingReceiveSucceeded(IncomingReplicationHandler instance)
        {
            _incomingLastActivityTime.AddOrUpdate(instance.ConnectionInfo, DateTime.UtcNow, (_, __) => DateTime.UtcNow);

            // PERF: _incoming locks if you do _incoming.Values. Using .Select
            // directly and fetching the Value avoids this problem.
            foreach (var kv in _incoming)
            {
                var handler = kv.Value;
                if (handler != instance)
                {
                    handler.OnReplicationFromAnotherSource();
                }
            }
        }
        private void IncomingDocumentsReceived(IncomingReplicationHandler handler)
        {
            if (_incoming.TryGetValue(handler.ConnectionInfo.SourceDatabaseId, out var stats) == false)
            {
                // possible?
                return;
            }

            var latestStat = stats.Handler.GetLatestReplicationPerformance();

            if (latestStat != null)
            {
                stats.Performance.Add(latestStat, _cts.Token);
            }
        }
示例#8
0
        private void OnIncomingReceiveFailed(IncomingReplicationHandler instance, Exception e)
        {
            using (instance)
            {
                IncomingReplicationHandler _;
                _incoming.TryRemove(instance.ConnectionInfo.SourceDatabaseId, out _);

                instance.Failed            -= OnIncomingReceiveFailed;
                instance.DocumentsReceived -= OnIncomingReceiveSucceeded;
                if (_log.IsInfoEnabled)
                {
                    _log.Info($"Incoming replication handler has thrown an unhandled exception. ({instance.FromToString})", e);
                }
            }
        }
示例#9
0
        public void Remote_change_vector_with_different_dbId_set_than_local_should_return_Conflict_at_conflict_status()
        {
            var dbIds = new List <Guid> {
                Guid.NewGuid(), Guid.NewGuid()
            };
            var local = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 10
                },
            };

            var remote = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 10
                }
            };

            Assert.Equal(IncomingReplicationHandler.ConflictStatus.Conflict, IncomingReplicationHandler.GetConflictStatus(remote, local));
        }
示例#10
0
        public void Remote_change_vector_smaller_than_local_and_some_remote_etags_higher_than_local_should_return_Conflict_at_conflict_status()
        {
            var dbIds = new List <Guid> {
                Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()
            };

            var local = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 10
                },
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 20
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 3000
                },
                new ChangeVectorEntry {
                    DbId = dbIds[3], Etag = 40
                }
            };

            var remote = new[]
            {
                new ChangeVectorEntry {
                    DbId = dbIds[0], Etag = 100
                },
                new ChangeVectorEntry {
                    DbId = dbIds[1], Etag = 200
                },
                new ChangeVectorEntry {
                    DbId = dbIds[2], Etag = 300
                }
            };

            Assert.Equal(IncomingReplicationHandler.ConflictStatus.Conflict, IncomingReplicationHandler.GetConflictStatus(remote, local));
        }
 private void IncomingHandlerAdded(IncomingReplicationHandler handler)
 {
     handler.HandleReplicationPulse += HandleReplicationPulse;
 }
 private void IncomingHandlerRemoved(IncomingReplicationHandler handler)
 {
     handler.HandleReplicationPulse -= HandleReplicationPulse;
 }
示例#13
0
        public void AcceptIncomingConnection(TcpConnectionOptions tcpConnectionOptions)
        {
            ReplicationLatestEtagRequest getLatestEtagMessage;

            using (tcpConnectionOptions.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                using (var readerObject = context.ParseToMemory(
                           tcpConnectionOptions.Stream,
                           "IncomingReplication/get-last-etag-message read",
                           BlittableJsonDocumentBuilder.UsageMode.None,
                           tcpConnectionOptions.PinnedBuffer))
                {
                    getLatestEtagMessage = JsonDeserializationServer.ReplicationLatestEtagRequest(readerObject);
                    if (_log.IsInfoEnabled)
                    {
                        _log.Info(
                            $"GetLastEtag: {getLatestEtagMessage.SourceTag}({getLatestEtagMessage.SourceMachineName}) / {getLatestEtagMessage.SourceDatabaseName} ({getLatestEtagMessage.SourceDatabaseId}) - {getLatestEtagMessage.SourceUrl}");
                    }
                }

            var connectionInfo = IncomingConnectionInfo.FromGetLatestEtag(getLatestEtagMessage);

            try
            {
                AssertValidConnection(connectionInfo);
            }
            catch (Exception e)
            {
                if (_log.IsInfoEnabled)
                {
                    _log.Info($"Connection from [{connectionInfo}] is rejected.", e);
                }

                var incomingConnectionRejectionInfos = _incomingRejectionStats.GetOrAdd(connectionInfo,
                                                                                        _ => new ConcurrentQueue <IncomingConnectionRejectionInfo>());
                incomingConnectionRejectionInfos.Enqueue(new IncomingConnectionRejectionInfo {
                    Reason = e.ToString()
                });

                try
                {
                    tcpConnectionOptions.Dispose();
                }
                catch
                {
                    // do nothing
                }

                throw;
            }

            try
            {
                using (Database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext documentsOperationContext))
                    using (Database.ConfigurationStorage.ContextPool.AllocateOperationContext(out TransactionOperationContext configurationContext))
                        using (var writer = new BlittableJsonTextWriter(documentsOperationContext, tcpConnectionOptions.Stream))
                            using (documentsOperationContext.OpenReadTransaction())
                                using (configurationContext.OpenReadTransaction())
                                {
                                    var changeVector = DocumentsStorage.GetDatabaseChangeVector(documentsOperationContext);

                                    var lastEtagFromSrc = Database.DocumentsStorage.GetLastReplicateEtagFrom(
                                        documentsOperationContext, getLatestEtagMessage.SourceDatabaseId);
                                    if (_log.IsInfoEnabled)
                                    {
                                        _log.Info($"GetLastEtag response, last etag: {lastEtagFromSrc}");
                                    }
                                    var response = new DynamicJsonValue
                                    {
                                        [nameof(ReplicationMessageReply.Type)]                 = "Ok",
                                        [nameof(ReplicationMessageReply.MessageType)]          = ReplicationMessageType.Heartbeat,
                                        [nameof(ReplicationMessageReply.LastEtagAccepted)]     = lastEtagFromSrc,
                                        [nameof(ReplicationMessageReply.NodeTag)]              = _server.NodeTag,
                                        [nameof(ReplicationMessageReply.DatabaseChangeVector)] = changeVector
                                    };

                                    documentsOperationContext.Write(writer, response);
                                    writer.Flush();
                                }
            }
            catch (Exception)
            {
                try
                {
                    tcpConnectionOptions.Dispose();
                }

                catch (Exception)
                {
                    // do nothing
                }
                throw;
            }

            var newIncoming = new IncomingReplicationHandler(
                tcpConnectionOptions,
                getLatestEtagMessage,
                this);

            newIncoming.Failed            += OnIncomingReceiveFailed;
            newIncoming.DocumentsReceived += OnIncomingReceiveSucceeded;

            if (_log.IsInfoEnabled)
            {
                _log.Info(
                    $"Initialized document replication connection from {connectionInfo.SourceDatabaseName} located at {connectionInfo.SourceUrl}");
            }

            // need to safeguard against two concurrent connection attempts
            var newConnection = _incoming.GetOrAdd(newIncoming.ConnectionInfo.SourceDatabaseId, newIncoming);

            if (newConnection == newIncoming)
            {
                newIncoming.Start();
                IncomingReplicationAdded?.Invoke(newIncoming);
                ForceTryReconnectAll();
            }
            else
            {
                newIncoming.Dispose();
            }
        }
示例#14
0
        public void AcceptIncomingConnection(TcpConnectionOptions tcpConnectionOptions)
        {
            ReplicationLatestEtagRequest getLatestEtagMessage;

            using (var readerObject = tcpConnectionOptions.MultiDocumentParser.ParseToMemory("IncomingReplication/get-last-etag-message read"))
            {
                getLatestEtagMessage = JsonDeserializationServer.ReplicationLatestEtagRequest(readerObject);
                if (_log.IsInfoEnabled)
                {
                    _log.Info($"GetLastEtag: {getLatestEtagMessage.SourceMachineName} / {getLatestEtagMessage.SourceDatabaseName} ({getLatestEtagMessage.SourceDatabaseId}) - {getLatestEtagMessage.SourceUrl}");
                }
            }

            var connectionInfo = IncomingConnectionInfo.FromGetLatestEtag(getLatestEtagMessage);

            try
            {
                AssertValidConnection(connectionInfo);
            }
            catch (Exception e)
            {
                if (_log.IsInfoEnabled)
                {
                    _log.Info($"Connection from [{connectionInfo}] is rejected.", e);
                }

                var incomingConnectionRejectionInfos = _incomingRejectionStats.GetOrAdd(connectionInfo,
                                                                                        _ => new ConcurrentQueue <IncomingConnectionRejectionInfo>());
                incomingConnectionRejectionInfos.Enqueue(new IncomingConnectionRejectionInfo {
                    Reason = e.ToString()
                });

                throw;
            }

            DocumentsOperationContext   documentsOperationContext;
            TransactionOperationContext configurationContext;

            using (_database.DocumentsStorage.ContextPool.AllocateOperationContext(out documentsOperationContext))
                using (_database.ConfigurationStorage.ContextPool.AllocateOperationContext(out configurationContext))
                    using (var writer = new BlittableJsonTextWriter(documentsOperationContext, tcpConnectionOptions.Stream))
                        using (var docTx = documentsOperationContext.OpenReadTransaction())
                            using (var configTx = configurationContext.OpenReadTransaction())
                            {
                                var documentsChangeVector = new DynamicJsonArray();
                                foreach (var changeVectorEntry in _database.DocumentsStorage.GetDatabaseChangeVector(documentsOperationContext))
                                {
                                    documentsChangeVector.Add(new DynamicJsonValue
                                    {
                                        [nameof(ChangeVectorEntry.DbId)] = changeVectorEntry.DbId.ToString(),
                                        [nameof(ChangeVectorEntry.Etag)] = changeVectorEntry.Etag
                                    });
                                }

                                var indexesChangeVector = new DynamicJsonArray();
                                var changeVectorAsArray = _database.IndexMetadataPersistence.GetIndexesAndTransformersChangeVector(configTx.InnerTransaction);
                                foreach (var changeVectorEntry in changeVectorAsArray)
                                {
                                    indexesChangeVector.Add(new DynamicJsonValue
                                    {
                                        [nameof(ChangeVectorEntry.DbId)] = changeVectorEntry.DbId.ToString(),
                                        [nameof(ChangeVectorEntry.Etag)] = changeVectorEntry.Etag
                                    });
                                }

                                var lastEtagFromSrc = _database.DocumentsStorage.GetLastReplicateEtagFrom(documentsOperationContext, getLatestEtagMessage.SourceDatabaseId);
                                if (_log.IsInfoEnabled)
                                {
                                    _log.Info($"GetLastEtag response, last etag: {lastEtagFromSrc}");
                                }
                                documentsOperationContext.Write(writer, new DynamicJsonValue
                                {
                                    [nameof(ReplicationMessageReply.Type)]             = "Ok",
                                    [nameof(ReplicationMessageReply.MessageType)]      = ReplicationMessageType.Heartbeat,
                                    [nameof(ReplicationMessageReply.LastEtagAccepted)] = lastEtagFromSrc,
                                    [nameof(ReplicationMessageReply.LastIndexTransformerEtagAccepted)] = _database.IndexMetadataPersistence.GetLastReplicateEtagFrom(configTx.InnerTransaction, getLatestEtagMessage.SourceDatabaseId),
                                    [nameof(ReplicationMessageReply.DocumentsChangeVector)]            = documentsChangeVector,
                                    [nameof(ReplicationMessageReply.IndexTransformerChangeVector)]     = indexesChangeVector
                                });
                                writer.Flush();
                            }

            var newIncoming = new IncomingReplicationHandler(
                tcpConnectionOptions.MultiDocumentParser,
                _database,
                tcpConnectionOptions.TcpClient,
                tcpConnectionOptions.Stream,
                getLatestEtagMessage,
                this);

            newIncoming.Failed            += OnIncomingReceiveFailed;
            newIncoming.DocumentsReceived += OnIncomingReceiveSucceeded;

            if (_log.IsInfoEnabled)
            {
                _log.Info($"Initialized document replication connection from {connectionInfo.SourceDatabaseName} located at {connectionInfo.SourceUrl}", null);
            }

            // need to safeguard against two concurrent connection attempts
            var newConnection = _incoming.GetOrAdd(newIncoming.ConnectionInfo.SourceDatabaseId, newIncoming);

            if (newConnection == newIncoming)
            {
                newIncoming.Start();
            }
            else
            {
                newIncoming.Dispose();
            }
        }