/// <summary> Append one or more events to the stream. </summary>
        /// <remarks>
        /// Events are assigned consecutive sequence numbers. The FIRST of these
        /// numbers is returned.
        ///
        /// If no events are provided, return null.
        ///
        /// If this object's state no longer represents the remote stream (because other
        /// events have been written from elsewhere), this method will not write any
        /// events and will return null. The caller should call <see cref="FetchAsync"/>
        /// until it returns false to have the object catch up with remote state.
        /// </remarks>
        public async Task <uint?> WriteAsync(IReadOnlyList <TEvent> events, CancellationToken cancel = default(CancellationToken))
        {
            if (events.Count == 0)
            {
                return(null);
            }
            if (Position < _minimumWritePosition)
            {
                return(null);
            }

            if (events.Any(e => e == null))
            {
                throw new ArgumentException(@"No null events allowed", nameof(events));
            }

            var sw = Stopwatch.StartNew();

            var rawEvents = events.Select((e, i) => new RawEvent(_lastSequence + (uint)(i + 1), _serializer.Serialize(e)))
                            .ToArray();

            try
            {
                var result = await Storage.WriteAsync(Position, rawEvents, cancel);

                _minimumWritePosition = result.NextPosition;

                if (result.Success)
                {
                    foreach (var e in rawEvents)
                    {
                        _cache.Enqueue(e);
                    }
                    Position = result.NextPosition;

                    var first = _lastSequence + 1;

                    _lastSequence += (uint)rawEvents.Length;

                    _log?.Debug(
                        $"Wrote {rawEvents.Length} events up to seq {_lastSequence} in {sw.Elapsed.TotalSeconds:F3}s.");

                    return(first);
                }

                _log?.Debug($"Collision when writing {rawEvents.Length} events after {sw.Elapsed.TotalSeconds:F3}s.");

                return(null);
            }
            catch (Exception e)
            {
                _log?.Error($"When writing {rawEvents.Length} events after seq {_lastSequence}.", e);
                throw;
            }
        }
示例#2
0
        public async Task <DriverWriteResult> WriteAsync(long position, IEnumerable <RawEvent> events, CancellationToken cancel = new CancellationToken())
        {
            var sw = Stopwatch.StartNew();

            try
            {
                return(await Inner.WriteAsync(position, events, cancel));
            }
            finally
            {
                Trace.WriteLine("WriteAsync " + sw.ElapsedMilliseconds);
            }
        }
示例#3
0
        /// <summary> Appends events to the stream. </summary>
        /// <remarks>
        /// The sequence number may not be before the current one.
        /// You can provide an arbitrarily large number of events, which will then be
        /// split into multiple writes.
        /// </remarks>
        public async Task WriteAsync(
            IEnumerable <KeyValuePair <uint, TEvent> > events,
            CancellationToken cancel = default(CancellationToken))
        {
            var sequence = _sequence ?? await LastWrittenAsync(cancel).ConfigureAwait(false);

            var position = (long)(_position ??
                                  (_position = await _driver.GetPositionAsync(cancel).ConfigureAwait(false)));

            // If an exception is thrown before we return properly, it might leave the
            // cached values (_sequence and _position) in an invalid state, so we null
            // them to cause a re-load on the next call. If returning properly, we'll
            // set them back to the proper values.
            _sequence = null;
            _position = null;

            var list      = new List <RawEvent>();
            var otherList = new List <RawEvent>();

            // Assigned the current writing task every time 'write' is called.
            Task writing = Task.FromResult(0);

            // Writes the events in the list to the driver
            Func <IReadOnlyList <RawEvent>, Task> write = async written =>
            {
                if (written.Count == 0)
                {
                    return;
                }

                var result = await _driver.WriteAsync(position, written, cancel)
                             .ConfigureAwait(false);

                if (!result.Success)
                {
                    throw new Exception(
                              $"Error writing events {written[0].Sequence} to {written[written.Count - 1].Sequence}");
                }

                position = result.NextPosition;
            };

            foreach (var kv in events)
            {
                cancel.ThrowIfCancellationRequested();

                var e   = kv.Value;
                var seq = kv.Key;

                if (seq <= sequence)
                {
                    throw new ArgumentException($"Out-of-order sequence #{seq}", nameof(events));
                }

                sequence = seq;
                list.Add(new RawEvent(seq, _serializer.Serialize(e)));

                // Avoid runaway scheduling (having to write increasingly
                // large sets of events because write is slower than enumeration
                // or serialization)
                //
                // Also, start a new write as soon as the previous one is done.
                if (writing.IsCompleted || list.Count > 1000)
                {
                    await writing.ConfigureAwait(false);

                    // The created task will ALWAYS be awaited, to guarantee
                    // that exceptions bubble up appropriately.
                    writing = write(list);

                    // Do not overwrite the list straight away, as it will
                    // be used during the write process. Instead, keep two
                    // lists and swap them (there is only one write process
                    // and one serialization process running at any given
                    // time, so two lists are enough).
                    var temp = list;
                    list      = otherList;
                    otherList = temp;

                    list.Clear();
                }
            }

            await writing.ConfigureAwait(false);

            await write(list).ConfigureAwait(false);

            // Cache the values we reached.
            _sequence = sequence;
            _position = position;
        }
 /// <see cref="IStorageDriver.WriteAsync"/>
 public Task <DriverWriteResult> WriteAsync(long position, IEnumerable <RawEvent> events, CancellationToken cancel = new CancellationToken()) =>
 _source.WriteAsync(position, events, cancel);