private bool TrySendLogBulk(ReplicaSubscription subscription, long masterCheckpoint) { /* * if (subscription == null) throw new Exception("subscription == null"); * if (subscription.BulkReader == null) throw new Exception("subscription.BulkReader == null"); * if (subscription.BulkReader.Chunk == null) throw new Exception("subscription.BulkReader.Chunk == null"); * if (subscription.DataBuffer == null) throw new Exception("subscription.DataBuffer == null"); */ var bulkReader = subscription.BulkReader; var chunkHeader = bulkReader.Chunk.ChunkHeader; BulkReadResult bulkResult; if (subscription.RawSend) { bulkResult = bulkReader.ReadNextRawBytes(subscription.DataBuffer.Length, subscription.DataBuffer); } else { var bytesToRead = (int)Math.Min(subscription.DataBuffer.Length, masterCheckpoint - subscription.LogPosition); bulkResult = bulkReader.ReadNextDataBytes(bytesToRead, subscription.DataBuffer); } bool dataFound = false; // for logical send we can get 0 at the end multiple time, but we need to get EOF exactly once if (bulkResult.BytesRead > 0 || (bulkResult.IsEOF && !subscription.RawSend && !subscription.EOFSent)) { var data = new byte[bulkResult.BytesRead]; Buffer.BlockCopy(subscription.DataBuffer, 0, data, 0, bulkResult.BytesRead); dataFound = true; subscription.EOFSent = bulkResult.IsEOF; if (subscription.RawSend) { var msg = new ReplicationMessage.RawChunkBulk( _instanceId, subscription.SubscriptionId, chunkHeader.ChunkStartNumber, chunkHeader.ChunkEndNumber, bulkResult.OldPosition, data, bulkResult.IsEOF); subscription.SendMessage(msg); } else { if (chunkHeader.GetLocalLogPosition(subscription.LogPosition) != bulkResult.OldPosition) { throw new Exception(string.Format("Replication invariant failure. SubscriptionPosition {0}, bulkResult.OldPosition {1}", subscription.LogPosition, bulkResult.OldPosition)); } var msg = new ReplicationMessage.DataChunkBulk( _instanceId, subscription.SubscriptionId, chunkHeader.ChunkStartNumber, chunkHeader.ChunkEndNumber, subscription.LogPosition, data, bulkResult.IsEOF); subscription.LogPosition += bulkResult.BytesRead; subscription.SendMessage(msg); } } if (bulkResult.IsEOF) { var newLogPosition = chunkHeader.ChunkEndPosition; if (newLogPosition < masterCheckpoint) { dataFound = true; SetSubscriptionPosition(subscription, newLogPosition, Guid.Empty, replicationStart: false, verbose: true, trial: 0); } } return(dataFound); }
private long SetSubscriptionPosition(ReplicaSubscription sub, long logPosition, Guid chunkId, bool replicationStart, bool verbose, int trial) { if (trial >= 10) { throw new Exception("Too many retrials to acquire reader for subscriber."); } try { var chunk = _db.Manager.GetChunkFor(logPosition); Debug.Assert(chunk != null, string.Format("Chunk for LogPosition {0} (0x{0:X}) is null in MasterReplicationService! Replica: [{1},C:{2},S:{3}]", logPosition, sub.ReplicaEndPoint, sub.ConnectionId, sub.SubscriptionId)); var bulkReader = chunk.AcquireReader(); if (chunk.ChunkHeader.IsScavenged && (chunkId == Guid.Empty || chunkId != chunk.ChunkHeader.ChunkId)) { var chunkStartPos = chunk.ChunkHeader.ChunkStartPosition; if (verbose) { Log.Info("Subscribed replica [{0}, S:{1}] for raw send at {2} (0x{2:X}) (requested {3} (0x{3:X})).", sub.ReplicaEndPoint, sub.SubscriptionId, chunkStartPos, logPosition); if (chunkStartPos != logPosition) { Log.Info("Forcing replica [{0}, S:{1}] to recreate chunk from position {2} (0x{2:X})...", sub.ReplicaEndPoint, sub.SubscriptionId, chunkStartPos); } } sub.LogPosition = chunkStartPos; sub.RawSend = true; bulkReader.SetRawPosition(ChunkHeader.Size); if (replicationStart) { sub.SendMessage(new ReplicationMessage.ReplicaSubscribed(_instanceId, sub.SubscriptionId, sub.LogPosition)); } sub.SendMessage(new ReplicationMessage.CreateChunk(_instanceId, sub.SubscriptionId, chunk.ChunkHeader, chunk.FileSize, isCompletedChunk: true)); } else { if (verbose) { Log.Info("Subscribed replica [{0},S:{1}] for data send at {2} (0x{2:X}).", sub.ReplicaEndPoint, sub.SubscriptionId, logPosition); } sub.LogPosition = logPosition; sub.RawSend = false; bulkReader.SetDataPosition(chunk.ChunkHeader.GetLocalLogPosition(logPosition)); if (replicationStart) { sub.SendMessage(new ReplicationMessage.ReplicaSubscribed(_instanceId, sub.SubscriptionId, sub.LogPosition)); } } sub.EOFSent = false; var oldBulkReader = Interlocked.Exchange(ref sub.BulkReader, bulkReader); if (oldBulkReader != null) { oldBulkReader.Release(); } return(sub.LogPosition); } catch (FileBeingDeletedException) { return(SetSubscriptionPosition(sub, logPosition, chunkId, replicationStart, verbose, trial + 1)); } }