void AppendToStream(IIdentity id, string envelopeId, Applied then, string explanation) { var stream = _factory.GetOrCreateStream(IdentityConvert.ToStream(id)); var b = new EnvelopeBuilder("unknown"); b.AddString("caused-by", envelopeId); if (!String.IsNullOrEmpty(explanation)) { b.AddString("explain", explanation); } foreach (var e in then.Events) { b.AddItem((object)e); } var data = _streamer.SaveEnvelopeData(b.Build()); Context.Debug("?? Append {0} at v{3} to '{1}' in thread {2}", then.Events.Count, IdentityConvert.ToStream(id), Thread.CurrentThread.ManagedThreadId, then.Version); if (!stream.TryAppend(data, TapeAppendCondition.VersionIs(then.Version))) { throw new InvalidOperationException("Failed to update the stream - it has been changed concurrently"); } }
public long TryAppend(byte[] buffer, TapeAppendCondition appendCondition = new TapeAppendCondition()) { if (buffer == null) throw new ArgumentNullException("buffer"); if (buffer.Length == 0) throw new ArgumentException("Buffer must contain at least one byte."); long version; int lastBlockSize; long offset; long firstVersion; long count; List<string> blockNames; var blob = _container.GetBlockBlobReference(_blobName); if (blob.Exists()) { var blockList = blob.DownloadBlockList().ToArray(); blockNames = blockList.Select(bl => bl.Name).ToList(); var lastBlock = blockList.LastOrDefault(); if (default(ListBlockItem) == lastBlock) { version = 0; lastBlockSize = int.MaxValue; offset = 0; firstVersion = 1; count = 0; } else { var nameInfo = Naming.GetInfo(DecodeName(lastBlock.Name)); firstVersion = nameInfo.FirstVersion; version = nameInfo.FirstVersion - 1 + nameInfo.Count; count = nameInfo.Count; if (lastBlock.Size > int.MaxValue) throw new InvalidOperationException("last block size must be in 'int' range"); lastBlockSize = (int) lastBlock.Size; offset = blockList.Reverse().Skip(1).Sum(l => l.Size); } } else { version = 0; lastBlockSize = int.MaxValue; offset = 0; firstVersion = 1; count = 0; blockNames = new List<string>(); } if (!appendCondition.Satisfy(version)) return 0; if (version > long.MaxValue - 1) throw new IndexOutOfRangeException("Version is more than long.MaxValue."); if (buffer.Length > FourMb) throw new ArgumentException("buffer size must be less than or equal to 4 Mb", "buffer"); using (var outStream = new MemoryStream()) { if (buffer.Length < MaxBlockSize && lastBlockSize <= MaxBlockSize - buffer.Length) { // read old block using (var s = blob.OpenRead()) { s.Seek(offset, SeekOrigin.Begin); s.CopyTo(outStream); TapeStreamSerializer.WriteRecord(outStream, buffer, version + 1); count++; blockNames.RemoveAt(blockNames.Count - 1); } } else { TapeStreamSerializer.WriteRecord(outStream, buffer, version + 1); firstVersion = version + 1; count = 1; } var blockId = EncodeName(Naming.GetName(firstVersion, count)); string md5Hash; outStream.Seek(0, SeekOrigin.Begin); using (var md5 = MD5.Create()) { md5Hash = Convert.ToBase64String(md5.ComputeHash(outStream)); } outStream.Seek(0, SeekOrigin.Begin); blob.PutBlock(blockId, outStream, md5Hash); blockNames.Add(blockId); blob.PutBlockList(blockNames); return version+1; } }
void Dispatch(string id, IEnumerable <ICommand> commands) { var stream = _factory.GetOrCreateStream(id); var records = stream.ReadRecords(0, int.MaxValue).ToList(); var events = records .Select(tr => _streamer.ReadAsEnvelopeData(tr.Data)) .SelectMany(i => i.Items) .Select(i => (IEvent)i.Content) .ToList(); var then = AggregateFactory .LoadProject(events, commands) .Select(e => new MessageBuilder(e.GetType(), e)).ToList(); if (then.Count == 0) { return; } // events are stored here as envelopes ) var b = new EnvelopeBuilder("unknown"); foreach (var e in then) { b.Items.Add(e); } var version = records.Count == 0 ? 0 : records.Last().Version; var data = _streamer.SaveEnvelopeData(b.Build()); var result = stream.TryAppend(data, TapeAppendCondition.VersionIs(version)); if (!result) { throw new InvalidOperationException( "Data was modified concurrently, and we don't have merging implemented, yet"); } var args = _path.Split(':'); IQueueWriterFactory factory; if (!_queue.TryGet(args[0], out factory)) { throw new InvalidOperationException("Not found " + _path); } var arVersion = events.Count + 1; var arName = id; for (int i = 0; i < then.Count; i++) { var name = string.Format("{0}/{1}/{2}", arName, arVersion, i); var builder = new EnvelopeBuilder(name); builder.Items.Add(then[i]); builder.AddString("from-entity", arName); factory.GetWriteQueue(args[1]).PutMessage(builder.Build()); } }