public void PreCommit(IList <PrepareLogRecord> commitedPrepares) { if (commitedPrepares.Count == 0) { return; } var lastPrepare = commitedPrepares[commitedPrepares.Count - 1]; string streamId = lastPrepare.EventStreamId; long eventNumber = EventNumber.Invalid; foreach (var prepare in commitedPrepares) { if (prepare.Flags.HasNoneOf(PrepareFlags.StreamDelete | PrepareFlags.Data)) { continue; } if (prepare.EventStreamId != streamId) { throw new Exception(string.Format("Expected stream: {0}, actual: {1}.", streamId, prepare.EventStreamId)); } eventNumber = prepare.ExpectedVersion + 1; /* for committed prepare expected version is always explicit */ _committedEvents.PutRecord(prepare.EventId, new EventInfo(streamId, eventNumber), throwOnDuplicate: false); } _notProcessedCommits.Enqueue(new CommitInfo(streamId, lastPrepare.LogPosition)); _streamVersions.Put(streamId, eventNumber, 1); if (SystemStreams.IsMetastream(streamId)) { var rawMeta = lastPrepare.Data; _streamRawMetas.Put(SystemStreams.OriginalStreamOf(streamId), new StreamMeta(rawMeta, null), +1); } }
public Task <RawStreamMetadataResult> GetStreamMetadataAsRawBytesAsync(string stream, UserCredentials userCredentials = null) { return(ReadEventAsync(SystemStreams.MetastreamOf(stream), -1, false, GetUserCredentials(_settings, userCredentials)).ContinueWith(t => { if (t.Exception != null) { throw t.Exception.InnerException; } var res = t.Result; switch (res.Status) { case EventReadStatus.Success: if (res.Event == null) { throw new Exception("Event is null while operation result is Success."); } var evnt = res.Event.Value.OriginalEvent; if (evnt == null) { return new RawStreamMetadataResult(stream, false, -1, Empty.ByteArray); } return new RawStreamMetadataResult(stream, false, evnt.EventNumber, evnt.Data); case EventReadStatus.NotFound: case EventReadStatus.NoStream: return new RawStreamMetadataResult(stream, false, -1, Empty.ByteArray); case EventReadStatus.StreamDeleted: return new RawStreamMetadataResult(stream, true, long.MaxValue, Empty.ByteArray); default: throw new ArgumentOutOfRangeException(string.Format("Unexpected ReadEventResult: {0}.", res.Status)); } })); }
private StreamMetadata GetStreamMetadataUncached(TFReaderLease reader, string streamId) { var metastreamId = SystemStreams.MetastreamOf(streamId); var metaEventNumber = GetStreamLastEventNumberCached(reader, metastreamId); if (metaEventNumber == ExpectedVersion.NoStream || metaEventNumber == EventNumber.DeletedStream) { return(StreamMetadata.Empty); } PrepareLogRecord prepare = ReadPrepareInternal(reader, metastreamId, metaEventNumber); if (prepare == null) { throw new Exception(string.Format("ReadPrepareInternal could not find metaevent #{0} on metastream '{1}'. " + "That should never happen.", metaEventNumber, metastreamId)); } if (prepare.Data.Length == 0 || prepare.Flags.HasNoneOf(PrepareFlags.IsJson)) { return(StreamMetadata.Empty); } try { var metadata = StreamMetadata.FromJsonBytes(prepare.Data); if (prepare.Version == LogRecordVersion.LogRecordV0 && metadata.TruncateBefore == int.MaxValue) { metadata = new StreamMetadata(metadata.MaxCount, metadata.MaxAge, EventNumber.DeletedStream, metadata.TempStream, metadata.CacheControl, metadata.Acl); } return(metadata); } catch (Exception) { return(StreamMetadata.Empty); } }
public void BeginMarkParkedMessagesReprocessed(long sequence, Action completed) { var metaStreamId = SystemStreams.MetastreamOf(ParkedStreamId); _ioDispatcher.WriteEvent( metaStreamId, ExpectedVersion.Any, CreateStreamMetadataEvent(sequence), SystemAccounts.System, msg => { switch (msg.Result) { case OperationResult.Success: _lastTruncateBefore = sequence; completed?.Invoke(); break; default: Log.Error("An error occured truncating the parked message stream {stream} due to {e}.", ParkedStreamId, msg.Result); Log.Error("Messages were not removed on retry"); completed?.Invoke(); break; } }); }
public Task <WriteResult> SetStreamMetadataAsync(string stream, long expectedMetastreamVersion, byte[] metadata, UserCredentials userCredentials = null) { Ensure.NotNullOrEmpty(stream, "stream"); if (SystemStreams.IsMetastream(stream)) { throw new ArgumentException(string.Format("Setting metadata for metastream '{0}' is not supported.", stream), "stream"); } var metaevent = new EventData(Guid.NewGuid(), SystemEventTypes.StreamMetadata, true, metadata ?? Empty.ByteArray, null); var metastream = SystemStreams.MetastreamOf(stream); var source = new TaskCompletionSource <WriteResult>(TaskCreationOptions.RunContinuationsAsynchronously); var envelope = new EmbeddedResponseEnvelope( new EmbeddedResponders.AppendToStream(source, metastream, expectedMetastreamVersion)); var corrId = Guid.NewGuid(); _publisher.PublishWithAuthentication(_authenticationProvider, GetUserCredentials(_settings, userCredentials), source.SetException, user => new ClientMessage.WriteEvents(corrId, corrId, envelope, false, metastream, expectedMetastreamVersion, metaevent.ConvertToEvent(), user)); return(source.Task); }
public StorageMessage.EffectiveAcl GetEffectiveAcl(string streamId) { using (var reader = _backend.BorrowReader()) { var sysSettings = _backend.GetSystemSettings() ?? SystemSettings.Default; StreamAcl acl; StreamAcl sysAcl; StreamAcl defAcl; var meta = GetStreamMetadataCached(reader, streamId); if (SystemStreams.IsSystemStream(streamId)) { defAcl = SystemSettings.Default.SystemStreamAcl; sysAcl = sysSettings.SystemStreamAcl ?? defAcl; acl = meta.Acl ?? sysAcl; } else { defAcl = SystemSettings.Default.UserStreamAcl; sysAcl = sysSettings.UserStreamAcl ?? defAcl; acl = meta.Acl ?? sysAcl; } return(new StorageMessage.EffectiveAcl(acl, sysAcl, defAcl)); } }
public async Task should_be_able_to_subscribe_to_all_with_volatile_subscription() { var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); EventStore.ClientAPI.ResolvedEvent receivedEvent = new EventStore.ClientAPI.ResolvedEvent(); var mre = new ManualResetEvent(false); await _store.SubscribeToAllAsync(true, (s, e) => { if (SystemStreams.IsSystemStream(e.OriginalStreamId)) { return(Task.CompletedTask); } receivedEvent = e; mre.Set(); return(Task.CompletedTask); }, userCredentials : DefaultData.AdminCredentials); await _store.AppendToStreamAsync(_volatileStreamTwo, intMaxValue + 2, evnt); Assert.That(mre.WaitOne(TimeSpan.FromSeconds(5)), "Timed out waiting for events to appear"); Assert.AreEqual(evnt.EventId, receivedEvent.Event.EventId); }
public async Task <WriteResult> SetStreamMetadataAsync(string stream, long expectedMetastreamVersion, byte[] metadata, UserCredentials userCredentials = null) { Ensure.NotNullOrEmpty(stream, "stream"); if (SystemStreams.IsMetastream(stream)) { throw new ArgumentException(string.Format("Setting metadata for metastream '{0}' is not supported.", stream), nameof(stream)); } var source = new TaskCompletionSource <WriteResult>(TaskCreationOptions.RunContinuationsAsynchronously); var metaevent = new EventData(Guid.NewGuid(), SystemEventTypes.StreamMetadata, true, metadata ?? Empty.ByteArray, null); var operation = new AppendToStreamOperation(Settings.Log, source, Settings.RequireMaster, SystemStreams.MetastreamOf(stream), expectedMetastreamVersion, new[] { metaevent }, userCredentials); await EnqueueOperation(operation).ConfigureAwait(false); return(await source.Task.ConfigureAwait(false)); }
public bool GetOrAddId(string name, out long streamNumber, out long createdId, out string createdName) { Ensure.NotNullOrEmpty(name, "name"); if (SystemStreams.IsMetastream(name)) { throw new ArgumentException(nameof(name)); } var oldNext = _next; streamNumber = _dict.GetOrAdd(name, n => { _next += 2; var streamNumber = _next; _dict[n] = streamNumber; _rev[streamNumber] = n; return(streamNumber); }); // return true if we found an existing entry. i.e. did not have to allocate from _next createdId = streamNumber; createdName = name; return(oldNext == _next); }
private ClientMessage.ReadEventCompleted ReadEvent(ClientMessage.ReadEvent msg) { using (HistogramService.Measure(_readerReadHistogram)) { try { var access = _readIndex.CheckStreamAccess(msg.EventStreamId, StreamAccessType.Read, msg.User); if (!access.Granted) { return(NoData(msg, ReadEventResult.AccessDenied)); } var result = _readIndex.ReadEvent(msg.EventStreamId, msg.EventNumber); var record = result.Result == ReadEventResult.Success && msg.ResolveLinkTos ? ResolveLinkToEvent(result.Record, msg.User, null) : ResolvedEvent.ForUnresolvedEvent(result.Record); if (record == null) { return(NoData(msg, ReadEventResult.AccessDenied)); } if ((result.Result == ReadEventResult.NoStream || result.Result == ReadEventResult.NotFound) && result.OriginalStreamExists && SystemStreams.IsSystemStream(msg.EventStreamId)) { return(NoData(msg, ReadEventResult.Success)); } return(new ClientMessage.ReadEventCompleted(msg.CorrelationId, msg.EventStreamId, result.Result, record.Value, result.Metadata, access.Public, null)); } catch (Exception exc) { Log.ErrorException(exc, "Error during processing ReadEvent request."); return(NoData(msg, ReadEventResult.Error, exc.Message)); } } }
private void SetMaxAge() { var metaStreamId = SystemStreams.MetastreamOf(SystemStreams.ScavengesStream); _ioDispatcher.ReadBackward(metaStreamId, -1, 1, false, SystemAccount.Principal, readResult => { if (readResult.Result == ReadStreamResult.Success || readResult.Result == ReadStreamResult.NoStream) { if (readResult.Events.Length == 1) { var currentMetadata = StreamMetadata.FromJsonBytes(readResult.Events[0].Event.Data); if (currentMetadata.MaxAge == _scavengeHistoryMaxAge) { Log.Debug("Max age already set for the {stream} stream.", SystemStreams.ScavengesStream); return; } } Log.Debug("Setting max age for the {stream} stream to {maxAge}.", SystemStreams.ScavengesStream, _scavengeHistoryMaxAge); var metadata = new StreamMetadata(maxAge: _scavengeHistoryMaxAge); var metaStreamEvent = new Event(Guid.NewGuid(), SystemEventTypes.StreamMetadata, isJson: true, data: metadata.ToJsonBytes(), metadata: null); _ioDispatcher.WriteEvent(metaStreamId, ExpectedVersion.Any, metaStreamEvent, SystemAccount.Principal, m => { if (m.Result != OperationResult.Success) { Log.Error( "Failed to write the $maxAge of {days} days metadata for the {stream} stream. Reason: {reason}", _scavengeHistoryMaxAge.TotalDays, SystemStreams.ScavengesStream, m.Result); } }); } }); }
public long LookupId(string streamName) { long streamId; if (SystemStreams.IsMetastream(streamName)) { streamName = SystemStreams.OriginalStreamOf(streamName); streamId = LookupId(streamName); return(_metastreams.MetaStreamOf(streamId)); } if (LogV3SystemStreams.TryGetVirtualStreamId(streamName, out streamId)) { return(streamId); } var result = _wrapped.LookupId(streamName); return(result == default ? SystemStreams.IsSystemStream(streamName) ? LogV3SystemStreams.NoSystemStream : LogV3SystemStreams.NoUserStream : result); }
public void ScavengeStarted(bool alwaysKeepScavenged, bool mergeChunks, int startFromChunk, int threads) { var metadataEventId = Guid.NewGuid(); var metaStreamId = SystemStreams.MetastreamOf(_streamName); var acl = new StreamAcl( new string[] { "$ops" }, new string[] { }, new string[] { }, new string[] { }, new string[] { } ); var metadata = new StreamMetadata(maxAge: _scavengeHistoryMaxAge, acl: acl); var metaStreamEvent = new Event(metadataEventId, SystemEventTypes.StreamMetadata, isJson: true, data: metadata.ToJsonBytes(), metadata: null); _ioDispatcher.WriteEvent(metaStreamId, ExpectedVersion.Any, metaStreamEvent, SystemAccounts.System, m => { if (m.Result != OperationResult.Success) { Log.Error( "Failed to write the $maxAge of {days} days metadata for the {stream} stream. Reason: {reason}", _scavengeHistoryMaxAge.TotalDays, _streamName, m.Result); } }); var scavengeStartedEvent = new Event(Guid.NewGuid(), SystemEventTypes.ScavengeStarted, true, new Dictionary <string, object> { { "scavengeId", _scavengeId }, { "nodeEndpoint", _nodeId }, { "alwaysKeepScavenged", alwaysKeepScavenged }, { "mergeChunks", mergeChunks }, { "startFromChunk", startFromChunk }, { "threads", threads }, }.ToJsonBytes(), null); WriteScavengeDetailEvent(_streamName, scavengeStartedEvent, _retryAttempts); }
public void Handle(ClientMessage.DeleteStream message) { List <EventRecord> list; if (_deletedStreams.Contains(message.EventStreamId)) { message.Envelope.ReplyWith(new ClientMessage.DeleteStreamCompleted(message.CorrelationId, OperationResult.StreamDeleted, string.Empty)); return; } if (!_streams.TryGetValue(message.EventStreamId, out list) || list == null) { message.Envelope.ReplyWith(new ClientMessage.DeleteStreamCompleted(message.CorrelationId, OperationResult.WrongExpectedVersion, string.Empty)); return; } _deletedStreams.Add(message.EventStreamId); ProcessWrite <Message>( null, message.CorrelationId, SystemStreams.MetastreamOf(message.EventStreamId), ExpectedVersion.Any, new Event[] { new Event(Guid.NewGuid(), SystemEventTypes.StreamMetadata, true, CreateStreamDeletedEventJson(), null) }, null, null); message.Envelope.ReplyWith(new ClientMessage.DeleteStreamCompleted(message.CorrelationId, OperationResult.Success, string.Empty)); }
private static void UpdateStreamAclWithRetry( this IODispatcher ioDispatcher, CancellationScope cancellationScope, string streamId, int expectedVersion, IPrincipal principal, StreamMetadata metadata, Action <ClientMessage.WriteEventsCompleted> handler, IEnumerator <Step> steps) { PerformWithRetry( ioDispatcher, handler, steps, expectedVersion == ExpectedVersion.Any, TimeSpan.FromMilliseconds(100), action => cancellationScope.Register( ioDispatcher.WriteEvents( SystemStreams.MetastreamOf(streamId), expectedVersion, new[] { new Event(Guid.NewGuid(), SystemEventTypes.StreamMetadata, true, metadata.ToJsonBytes(), null) }, principal, response => { if (cancellationScope.Cancelled(response.CorrelationId)) { return; } action(response, response.Result); }))); }
protected EventRecord WriteStreamMetadata(string eventStreamId, int eventNumber, string metadata, DateTime?timestamp = null) { var prepare = LogRecord.SingleWrite(WriterCheckpoint.ReadNonFlushed(), Guid.NewGuid(), Guid.NewGuid(), SystemStreams.MetastreamOf(eventStreamId), eventNumber - 1, SystemEventTypes.StreamMetadata, Helper.UTF8NoBom.GetBytes(metadata), null, timestamp ?? DateTime.UtcNow, PrepareFlags.IsJson); long pos; Assert.IsTrue(Writer.Write(prepare, out pos)); var commit = LogRecord.Commit(WriterCheckpoint.ReadNonFlushed(), prepare.CorrelationId, prepare.LogPosition, eventNumber); Assert.IsTrue(Writer.Write(commit, out pos)); var eventRecord = new EventRecord(eventNumber, prepare); return(eventRecord); }
public async Task reads_all_existing_events_after_position_and_keep_listening_to_new_ones() { var events = _fixture.CreateTestEvents(20).ToArray(); var appeared = new TaskCompletionSource <bool>(); var dropped = new TaskCompletionSource <(SubscriptionDroppedReason, Exception)>(); var beforeEvents = events.Take(10); var afterEvents = events.Skip(10); using var enumerator = events.AsEnumerable().GetEnumerator(); enumerator.MoveNext(); var position = await _fixture.Client.ReadAllAsync(Direction.Forwards, Position.Start, 1) .Select(x => x.OriginalEvent.Position) .FirstAsync(); foreach (var @event in beforeEvents) { await _fixture.Client.AppendToStreamAsync($"stream-{@event.EventId:n}", AnyStreamRevision.NoStream, new[] { @event }); } using var subscription = await _fixture.Client.SubscribeToAllAsync(position, EventAppeared, false, SubscriptionDropped); foreach (var @event in afterEvents) { await _fixture.Client.AppendToStreamAsync($"stream-{@event.EventId:n}", AnyStreamRevision.NoStream, new[] { @event }); } await appeared.Task.WithTimeout(); Assert.False(dropped.Task.IsCompleted); subscription.Dispose(); var(reason, ex) = await dropped.Task.WithTimeout(); Assert.Equal(SubscriptionDroppedReason.Disposed, reason); Assert.Null(ex); Task EventAppeared(StreamSubscription s, ResolvedEvent e, CancellationToken ct) { if (position >= e.OriginalEvent.Position) { appeared.TrySetException(new Exception()); } if (!SystemStreams.IsSystemStream(e.OriginalStreamId)) { try { Assert.Equal(enumerator.Current.EventId, e.OriginalEvent.EventId); if (!enumerator.MoveNext()) { appeared.TrySetResult(true); } } catch (Exception ex) { appeared.TrySetException(ex); throw; } } return(Task.CompletedTask); } void SubscriptionDropped(StreamSubscription s, SubscriptionDroppedReason reason, Exception ex) => dropped.SetResult((reason, ex)); }
private StreamAccess CheckStreamAccessInternal(TFReaderLease reader, string streamId, StreamAccessType streamAccessType, IPrincipal user) { if (SystemStreams.IsMetastream(streamId)) { switch (streamAccessType) { case StreamAccessType.Read: return(CheckStreamAccessInternal(reader, SystemStreams.OriginalStreamOf(streamId), StreamAccessType.MetaRead, user)); case StreamAccessType.Write: return(CheckStreamAccessInternal(reader, SystemStreams.OriginalStreamOf(streamId), StreamAccessType.MetaWrite, user)); case StreamAccessType.Delete: case StreamAccessType.MetaRead: case StreamAccessType.MetaWrite: return(new StreamAccess(false)); default: throw new ArgumentOutOfRangeException("streamAccessType"); } } if ((streamAccessType == StreamAccessType.Write || streamAccessType == StreamAccessType.Delete) && streamId == SystemStreams.AllStream) { return(new StreamAccess(false)); } var sysSettings = _cache.GetSystemSettings() ?? SystemSettings.Default; var meta = GetStreamMetadataCached(reader, streamId); StreamAcl acl; StreamAcl sysAcl; StreamAcl defAcl; if (SystemStreams.IsSystemStream(streamId)) { defAcl = SystemSettings.Default.SystemStreamAcl; sysAcl = sysSettings.SystemStreamAcl ?? defAcl; acl = meta.Acl ?? sysAcl; } else { defAcl = SystemSettings.Default.UserStreamAcl; sysAcl = sysSettings.UserStreamAcl ?? defAcl; acl = meta.Acl ?? sysAcl; } string[] roles; switch (streamAccessType) { case StreamAccessType.Read: roles = acl.ReadRoles ?? sysAcl.ReadRoles ?? defAcl.ReadRoles; break; case StreamAccessType.Write: roles = acl.WriteRoles ?? sysAcl.WriteRoles ?? defAcl.WriteRoles; break; case StreamAccessType.Delete: roles = acl.DeleteRoles ?? sysAcl.DeleteRoles ?? defAcl.DeleteRoles; break; case StreamAccessType.MetaRead: roles = acl.MetaReadRoles ?? sysAcl.MetaReadRoles ?? defAcl.MetaReadRoles; break; case StreamAccessType.MetaWrite: roles = acl.MetaWriteRoles ?? sysAcl.MetaWriteRoles ?? defAcl.MetaWriteRoles; break; default: throw new ArgumentOutOfRangeException("streamAccessType"); } var isPublic = roles.Contains(x => x == SystemRoles.All); if (isPublic) { return(new StreamAccess(true, true)); } if (user == null) { return(new StreamAccess(false)); } if (user.IsInRole(SystemRoles.Admins)) { return(new StreamAccess(true)); } for (int i = 0; i < roles.Length; ++i) { if (user.IsInRole(roles[i])) { return(new StreamAccess(true)); } } return(new StreamAccess(false)); }
public CommitCheckResult CheckCommit(string streamId, long expectedVersion, IEnumerable <Guid> eventIds) { var curVersion = GetStreamLastEventNumber(streamId); if (curVersion == EventNumber.DeletedStream) { return(new CommitCheckResult(CommitDecision.Deleted, streamId, curVersion, -1, -1, false)); } if (curVersion == EventNumber.Invalid) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } if (expectedVersion == ExpectedVersion.StreamExists) { if (IsSoftDeleted(streamId)) { return(new CommitCheckResult(CommitDecision.Deleted, streamId, curVersion, -1, -1, true)); } if (curVersion < 0) { var metadataVersion = GetStreamLastEventNumber(SystemStreams.MetastreamOf(streamId)); if (metadataVersion < 0) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } } } // idempotency checks if (expectedVersion == ExpectedVersion.Any || expectedVersion == ExpectedVersion.StreamExists) { var first = true; long startEventNumber = -1; long endEventNumber = -1; foreach (var eventId in eventIds) { EventInfo prepInfo; if (!_committedEvents.TryGetRecord(eventId, out prepInfo) || prepInfo.StreamId != streamId) { return(new CommitCheckResult(first ? CommitDecision.Ok : CommitDecision.CorruptedIdempotency, streamId, curVersion, -1, -1, first && IsSoftDeleted(streamId))); } if (first) { startEventNumber = prepInfo.EventNumber; } endEventNumber = prepInfo.EventNumber; first = false; } if (first) /*no data in transaction*/ { return(new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, IsSoftDeleted(streamId))); } else { var isReplicated = _indexReader.GetStreamLastEventNumber(streamId) >= endEventNumber; //TODO(clc): the new index should hold the log positions removing this read //n.b. the index will never have the event in the case of NotReady as it only committed records are indexed //in that case the position will need to come from the pre-index var idempotentEvent = _indexReader.ReadEvent(streamId, endEventNumber); var logPos = idempotentEvent.Result == ReadEventResult.Success ? idempotentEvent.Record.LogPosition : -1; if (isReplicated) { return(new CommitCheckResult(CommitDecision.Idempotent, streamId, curVersion, startEventNumber, endEventNumber, false, logPos)); } else { return(new CommitCheckResult(CommitDecision.IdempotentNotReady, streamId, curVersion, startEventNumber, endEventNumber, false, logPos)); } } } if (expectedVersion < curVersion) { var eventNumber = expectedVersion; foreach (var eventId in eventIds) { eventNumber += 1; EventInfo prepInfo; if (_committedEvents.TryGetRecord(eventId, out prepInfo) && prepInfo.StreamId == streamId && prepInfo.EventNumber == eventNumber) { continue; } var res = _indexReader.ReadPrepare(streamId, eventNumber); if (res != null && res.EventId == eventId) { continue; } var first = eventNumber == expectedVersion + 1; if (!first) { return(new CommitCheckResult(CommitDecision.CorruptedIdempotency, streamId, curVersion, -1, -1, false)); } if (expectedVersion == ExpectedVersion.NoStream && IsSoftDeleted(streamId)) { return(new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, true)); } return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } if (eventNumber == expectedVersion) /* no data in transaction */ { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } else { var isReplicated = _indexReader.GetStreamLastEventNumber(streamId) >= eventNumber; //TODO(clc): the new index should hold the log positions removing this read //n.b. the index will never have the event in the case of NotReady as it only committed records are indexed //in that case the position will need to come from the pre-index var idempotentEvent = _indexReader.ReadEvent(streamId, eventNumber); var logPos = idempotentEvent.Result == ReadEventResult.Success ? idempotentEvent.Record.LogPosition : -1; if (isReplicated) { return(new CommitCheckResult(CommitDecision.Idempotent, streamId, curVersion, expectedVersion + 1, eventNumber, false, logPos)); } else { return(new CommitCheckResult(CommitDecision.IdempotentNotReady, streamId, curVersion, expectedVersion + 1, eventNumber, false, logPos)); } } } if (expectedVersion > curVersion) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } // expectedVersion == currentVersion return(new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, IsSoftDeleted(streamId))); }
public string OriginalStreamOf(string streamId) => SystemStreams.OriginalStreamOf(streamId);
public DeleteWithExistingMetadata() { _streamName = Guid.NewGuid().ToString(); _metadataStreamName = SystemStreams.MetastreamOf(_streamName); }
public bool IsSystemStream(string streamId) => SystemStreams.IsSystemStream(streamId);
public string MetaStreamOf(string streamId) => SystemStreams.MetastreamOf(streamId);
public CommitCheckResult CheckCommit(string streamId, long expectedVersion, IEnumerable <Guid> eventIds) { var curVersion = GetStreamLastEventNumber(streamId); if (curVersion == EventNumber.DeletedStream) { return(new CommitCheckResult(CommitDecision.Deleted, streamId, curVersion, -1, -1, false)); } if (curVersion == EventNumber.Invalid) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } if (expectedVersion == ExpectedVersion.StreamExists) { if (IsSoftDeleted(streamId)) { return(new CommitCheckResult(CommitDecision.Deleted, streamId, curVersion, -1, -1, true)); } if (curVersion < 0) { var metadataVersion = GetStreamLastEventNumber(SystemStreams.MetastreamOf(streamId)); if (metadataVersion < 0) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } } } // idempotency checks if (expectedVersion == ExpectedVersion.Any || expectedVersion == ExpectedVersion.StreamExists) { var first = true; long startEventNumber = -1; long endEventNumber = -1; foreach (var eventId in eventIds) { EventInfo prepInfo; if (!_committedEvents.TryGetRecord(eventId, out prepInfo) || prepInfo.StreamId != streamId) { return(new CommitCheckResult(first ? CommitDecision.Ok : CommitDecision.CorruptedIdempotency, streamId, curVersion, -1, -1, first && IsSoftDeleted(streamId))); } if (first) { startEventNumber = prepInfo.EventNumber; } endEventNumber = prepInfo.EventNumber; first = false; } return(first /* no data in transaction */ ? new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, IsSoftDeleted(streamId)) : new CommitCheckResult(CommitDecision.Idempotent, streamId, curVersion, startEventNumber, endEventNumber, false)); } if (expectedVersion < curVersion) { var eventNumber = expectedVersion; foreach (var eventId in eventIds) { eventNumber += 1; EventInfo prepInfo; if (_committedEvents.TryGetRecord(eventId, out prepInfo) && prepInfo.StreamId == streamId && prepInfo.EventNumber == eventNumber) { continue; } var res = _indexReader.ReadPrepare(streamId, eventNumber); if (res != null && res.EventId == eventId) { continue; } var first = eventNumber == expectedVersion + 1; if (!first) { return(new CommitCheckResult(CommitDecision.CorruptedIdempotency, streamId, curVersion, -1, -1, false)); } if (expectedVersion == ExpectedVersion.NoStream && IsSoftDeleted(streamId)) { return(new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, true)); } return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } return(eventNumber == expectedVersion /* no data in transaction */ ? new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false) : new CommitCheckResult(CommitDecision.Idempotent, streamId, curVersion, expectedVersion + 1, eventNumber, false)); } if (expectedVersion > curVersion) { return(new CommitCheckResult(CommitDecision.WrongExpectedVersion, streamId, curVersion, -1, -1, false)); } // expectedVersion == currentVersion return(new CommitCheckResult(CommitDecision.Ok, streamId, curVersion, -1, -1, IsSoftDeleted(streamId))); }
void IHandle <StorageMessage.WritePrepares> .Handle(StorageMessage.WritePrepares msg) { Interlocked.Decrement(ref FlushMessagesInQueue); try { if (msg.LiveUntil < DateTime.UtcNow) { return; } string streamId = msg.EventStreamId; var commitCheck = _indexWriter.CheckCommit(streamId, msg.ExpectedVersion, msg.Events.Select(x => x.EventId)); if (commitCheck.Decision != CommitDecision.Ok) { ActOnCommitCheckFailure(msg.Envelope, msg.CorrelationId, commitCheck); return; } var prepares = new List <PrepareLogRecord>(); var logPosition = Writer.Checkpoint.ReadNonFlushed(); if (msg.Events.Length > 0) { var transactionPosition = logPosition; for (int i = 0; i < msg.Events.Length; ++i) { var evnt = msg.Events[i]; var flags = PrepareFlags.Data | PrepareFlags.IsCommitted; if (i == 0) { flags |= PrepareFlags.TransactionBegin; } if (i == msg.Events.Length - 1) { flags |= PrepareFlags.TransactionEnd; } if (evnt.IsJson) { flags |= PrepareFlags.IsJson; } // when IsCommitted ExpectedVersion is always explicit var expectedVersion = commitCheck.CurrentVersion + i; var res = WritePrepareWithRetry( LogRecord.Prepare(logPosition, msg.CorrelationId, evnt.EventId, transactionPosition, i, streamId, expectedVersion, flags, evnt.EventType, evnt.Data, evnt.Metadata)); logPosition = res.NewPos; if (i == 0) { transactionPosition = res.WrittenPos; } // transaction position could be changed due to switching to new chunk prepares.Add(res.Prepare); } } else { WritePrepareWithRetry( LogRecord.Prepare(logPosition, msg.CorrelationId, Guid.NewGuid(), logPosition, -1, streamId, commitCheck.CurrentVersion, PrepareFlags.TransactionBegin | PrepareFlags.TransactionEnd | PrepareFlags.IsCommitted, null, Empty.ByteArray, Empty.ByteArray)); } bool softUndeleteMetastream = SystemStreams.IsMetastream(streamId) && _indexWriter.IsSoftDeleted(SystemStreams.OriginalStreamOf(streamId)); _indexWriter.PreCommit(prepares); if (commitCheck.IsSoftDeleted) { SoftUndeleteStream(streamId, commitCheck.CurrentVersion + 1); } if (softUndeleteMetastream) { SoftUndeleteMetastream(streamId); } } catch (Exception exc) { Log.ErrorException(exc, "Exception in writer."); throw; } finally { Flush(); } }
public int Commit(IList <PrepareLogRecord> commitedPrepares) { int eventNumber = EventNumber.Invalid; if (commitedPrepares.Count == 0) { return(eventNumber); } var lastCommitPosition = Interlocked.Read(ref _lastCommitPosition); var lastPrepare = commitedPrepares[commitedPrepares.Count - 1]; string streamId = lastPrepare.EventStreamId; uint streamHash = _hasher.Hash(streamId); var indexEntries = new List <IndexEntry>(); var prepares = new List <PrepareLogRecord>(); foreach (var prepare in commitedPrepares) { if (prepare.Flags.HasNoneOf(PrepareFlags.StreamDelete | PrepareFlags.Data)) { continue; } if (prepare.EventStreamId != streamId) { throw new Exception(string.Format("Expected stream: {0}, actual: {1}.", streamId, prepare.EventStreamId)); } if (prepare.LogPosition < lastCommitPosition || (prepare.LogPosition == lastCommitPosition && !_indexRebuild)) { continue; // already committed } eventNumber = prepare.ExpectedVersion + 1; /* for committed prepare expected version is always explicit */ if (new TFPos(prepare.LogPosition, prepare.LogPosition) > new TFPos(_persistedCommitPos, _persistedPreparePos)) { indexEntries.Add(new IndexEntry(streamHash, eventNumber, prepare.LogPosition)); prepares.Add(prepare); } } if (indexEntries.Count > 0) { if (_additionalCommitChecks) { CheckStreamVersion(streamId, indexEntries[0].Version, null); // TODO AN: bad passing null commit CheckDuplicateEvents(streamHash, null, indexEntries, prepares); // TODO AN: bad passing null commit } _tableIndex.AddEntries(lastPrepare.LogPosition, indexEntries); // atomically add a whole bulk of entries } if (eventNumber != EventNumber.Invalid) { if (eventNumber < 0) { throw new Exception(string.Format("EventNumber {0} is incorrect.", eventNumber)); } _backend.SetStreamLastEventNumber(streamId, eventNumber); if (SystemStreams.IsMetastream(streamId)) { _backend.SetStreamMetadata(SystemStreams.OriginalStreamOf(streamId), null); // invalidate cached metadata } if (streamId == SystemStreams.SettingsStream) { _backend.SetSystemSettings(DeserializeSystemSettings(prepares[prepares.Count - 1].Data)); } } var newLastCommitPosition = Math.Max(lastPrepare.LogPosition, lastCommitPosition); if (Interlocked.CompareExchange(ref _lastCommitPosition, newLastCommitPosition, lastCommitPosition) != lastCommitPosition) { throw new Exception("Concurrency error in ReadIndex.Commit: _lastCommitPosition was modified during Commit execution!"); } for (int i = 0, n = indexEntries.Count; i < n; ++i) { _bus.Publish(new StorageMessage.EventCommited(prepares[i].LogPosition, new EventRecord(indexEntries[i].Version, prepares[i]))); } return(eventNumber); }
public long Commit(IList <PrepareLogRecord> commitedPrepares, bool isTfEof, bool cacheLastEventNumber) { long eventNumber = EventNumber.Invalid; if (commitedPrepares.Count == 0) { return(eventNumber); } var lastIndexedPosition = _indexChk.Read(); var lastPrepare = commitedPrepares[commitedPrepares.Count - 1]; string streamId = lastPrepare.EventStreamId; var indexEntries = new List <IndexKey>(); var prepares = new List <PrepareLogRecord>(); foreach (var prepare in commitedPrepares) { if (prepare.Flags.HasNoneOf(PrepareFlags.StreamDelete | PrepareFlags.Data)) { continue; } if (prepare.EventStreamId != streamId) { var sb = new StringBuilder(); sb.Append(string.Format("ERROR: Expected stream: {0}, actual: {1}.", streamId, prepare.EventStreamId)); sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); sb.Append("Prepares: (" + commitedPrepares.Count + ")"); sb.Append(Environment.NewLine); for (int i = 0; i < commitedPrepares.Count; i++) { var p = commitedPrepares[i]; sb.Append("Stream ID: " + p.EventStreamId); sb.Append(Environment.NewLine); sb.Append("LogPosition: " + p.LogPosition); sb.Append(Environment.NewLine); sb.Append("Flags: " + p.Flags); sb.Append(Environment.NewLine); sb.Append("Type: " + p.EventType); sb.Append(Environment.NewLine); sb.Append("MetaData: " + Encoding.UTF8.GetString(p.Metadata)); sb.Append(Environment.NewLine); sb.Append("Data: " + Encoding.UTF8.GetString(p.Data)); sb.Append(Environment.NewLine); } throw new Exception(sb.ToString()); } if (prepare.LogPosition < lastIndexedPosition || (prepare.LogPosition == lastIndexedPosition && !_indexRebuild)) { continue; // already committed } eventNumber = prepare.ExpectedVersion + 1; /* for committed prepare expected version is always explicit */ if (new TFPos(prepare.LogPosition, prepare.LogPosition) > new TFPos(_persistedCommitPos, _persistedPreparePos)) { indexEntries.Add(new IndexKey(streamId, eventNumber, prepare.LogPosition)); prepares.Add(prepare); } } if (indexEntries.Count > 0) { if (_additionalCommitChecks && cacheLastEventNumber) { CheckStreamVersion(streamId, indexEntries[0].Version, null); // TODO AN: bad passing null commit CheckDuplicateEvents(streamId, null, indexEntries, prepares); // TODO AN: bad passing null commit } _tableIndex.AddEntries(lastPrepare.LogPosition, indexEntries); // atomically add a whole bulk of entries } if (eventNumber != EventNumber.Invalid) { if (eventNumber < 0) { throw new Exception(string.Format("EventNumber {0} is incorrect.", eventNumber)); } if (cacheLastEventNumber) { _backend.SetStreamLastEventNumber(streamId, eventNumber); } if (SystemStreams.IsMetastream(streamId)) { _backend.SetStreamMetadata(SystemStreams.OriginalStreamOf(streamId), null); // invalidate cached metadata } if (streamId == SystemStreams.SettingsStream) { _backend.SetSystemSettings(DeserializeSystemSettings(prepares[prepares.Count - 1].Data)); } } var newLastIndexedPosition = Math.Max(lastPrepare.LogPosition, lastIndexedPosition); if (_indexChk.Read() != lastIndexedPosition) { throw new Exception( "Concurrency error in ReadIndex.Commit: _lastCommitPosition was modified during Commit execution!"); } _indexChk.Write(newLastIndexedPosition); _indexChk.Flush(); if (!_indexRebuild) { for (int i = 0, n = indexEntries.Count; i < n; ++i) { _bus.Publish( new StorageMessage.EventCommitted( prepares[i].LogPosition, new EventRecord(indexEntries[i].Version, prepares[i]), isTfEof && i == n - 1)); } } return(eventNumber); }
public DbResult CreateDb() { var records = new LogRecord[_chunkRecs.Count][]; for (int i = 0; i < records.Length; ++i) { records[i] = new LogRecord[_chunkRecs[i].Length]; } var transactions = new Dictionary <int, TransactionInfo>(); var streams = new Dictionary <string, StreamInfo>(); var streamUncommitedVersion = new Dictionary <string, long>(); for (int i = 0; i < _chunkRecs.Count; ++i) { for (int j = 0; j < _chunkRecs[i].Length; ++j) { var rec = _chunkRecs[i][j]; TransactionInfo transInfo; bool transCreate = transactions.TryGetValue(rec.Transaction, out transInfo); if (!transCreate) { if (rec.Type == Rec.RecType.Commit) { throw new Exception("Commit for non-existing transaction."); } transactions[rec.Transaction] = transInfo = new TransactionInfo(rec.StreamId, rec.Id, rec.Id); streams[rec.StreamId] = new StreamInfo(-1); streamUncommitedVersion[rec.StreamId] = -1; } else { if (rec.Type == Rec.RecType.TransStart) { throw new Exception(string.Format("Unexpected record type: {0}.", rec.Type)); } } if (transInfo.StreamId != rec.StreamId) { throw new Exception(string.Format("Wrong stream id for transaction. Transaction StreamId: {0}, record StreamId: {1}.", transInfo.StreamId, rec.StreamId)); } if (rec.Type != Rec.RecType.Commit && transInfo.IsDelete) { throw new Exception("Transaction with records after delete record."); } if (rec.Type == Rec.RecType.Delete) { transInfo.IsDelete = true; } transInfo.LastPrepareId = rec.Id; } } for (int i = 0; i < _chunkRecs.Count; ++i) { var chunk = i == 0 ? _db.Manager.GetChunk(0) : _db.Manager.AddNewChunk(); _db.Config.WriterCheckpoint.Write(i * (long)_db.Config.ChunkSize); for (int j = 0; j < _chunkRecs[i].Length; ++j) { var rec = _chunkRecs[i][j]; var transInfo = transactions[rec.Transaction]; var logPos = _db.Config.WriterCheckpoint.ReadNonFlushed(); long streamVersion = streamUncommitedVersion[rec.StreamId]; if (streamVersion == -1 && rec.Type != Rec.RecType.TransStart && rec.Type != Rec.RecType.Prepare && rec.Type != Rec.RecType.Delete) { throw new Exception(string.Format("Stream {0} is empty.", rec.StreamId)); } if (streamVersion == EventNumber.DeletedStream && rec.Type != Rec.RecType.Commit) { throw new Exception(string.Format("Stream {0} was deleted, but we need to write some more prepares.", rec.StreamId)); } if (transInfo.FirstPrepareId == rec.Id) { transInfo.TransactionPosition = logPos; transInfo.TransactionEventNumber = streamVersion + 1; transInfo.TransactionOffset = 0; } LogRecord record; var expectedVersion = transInfo.FirstPrepareId == rec.Id ? streamVersion : ExpectedVersion.Any; switch (rec.Type) { case Rec.RecType.Prepare: { record = CreateLogRecord(rec, transInfo, logPos, expectedVersion); if (SystemStreams.IsMetastream(rec.StreamId)) { transInfo.StreamMetadata = rec.Metadata; } streamUncommitedVersion[rec.StreamId] += 1; break; } case Rec.RecType.Delete: { record = CreateLogRecord(rec, transInfo, logPos, expectedVersion); streamUncommitedVersion[rec.StreamId] = rec.Version == LogRecordVersion.LogRecordV0 ? int.MaxValue : EventNumber.DeletedStream; break; } case Rec.RecType.TransStart: case Rec.RecType.TransEnd: { record = CreateLogRecord(rec, transInfo, logPos, expectedVersion); break; } case Rec.RecType.Commit: { record = CreateLogRecord(rec, transInfo, logPos, expectedVersion); if (transInfo.StreamMetadata != null) { var streamId = SystemStreams.OriginalStreamOf(rec.StreamId); if (!streams.ContainsKey(streamId)) { streams.Add(streamId, new StreamInfo(-1)); } streams[streamId].StreamMetadata = transInfo.StreamMetadata; } if (transInfo.IsDelete) { streams[rec.StreamId].StreamVersion = EventNumber.DeletedStream; } else { streams[rec.StreamId].StreamVersion = transInfo.TransactionEventNumber + transInfo.TransactionOffset - 1; } break; } default: throw new ArgumentOutOfRangeException(); } var writerRes = chunk.TryAppend(record); if (!writerRes.Success) { throw new Exception(string.Format("Could not write log record: {0}", record)); } _db.Config.WriterCheckpoint.Write(i * (long)_db.Config.ChunkSize + writerRes.NewPosition); records[i][j] = record; } if (i < _chunkRecs.Count - 1 || (_completeLast && i == _chunkRecs.Count - 1)) { chunk.Complete(); } else { chunk.Flush(); } } return(new DbResult(_db, records, streams)); }
private void PublishWriteMetaStream() { _ioDispatcher.WriteEvent( SystemStreams.MetastreamOf(_streamId), ExpectedVersion.Any, _submittedWriteMetaStreamEvent, _writeAs, HandleMetadataWriteCompleted); }
public long Commit(CommitLogRecord commit, bool isTfEof, bool cacheLastEventNumber) { long eventNumber = EventNumber.Invalid; var lastIndexedPosition = _indexChk.Read(); if (commit.LogPosition < lastIndexedPosition || (commit.LogPosition == lastIndexedPosition && !_indexRebuild)) { return(eventNumber); // already committed } string streamId = null; var indexEntries = new List <IndexKey>(); var prepares = new List <PrepareLogRecord>(); foreach (var prepare in GetTransactionPrepares(commit.TransactionPosition, commit.LogPosition)) { if (prepare.Flags.HasNoneOf(PrepareFlags.StreamDelete | PrepareFlags.Data)) { continue; } if (streamId == null) { streamId = prepare.EventStreamId; } else { if (prepare.EventStreamId != streamId) { throw new Exception(string.Format("Expected stream: {0}, actual: {1}. LogPosition: {2}", streamId, prepare.EventStreamId, commit.LogPosition)); } } eventNumber = prepare.Flags.HasAllOf(PrepareFlags.StreamDelete) ? EventNumber.DeletedStream : commit.FirstEventNumber + prepare.TransactionOffset; if (new TFPos(commit.LogPosition, prepare.LogPosition) > new TFPos(_persistedCommitPos, _persistedPreparePos)) { indexEntries.Add(new IndexKey(streamId, eventNumber, prepare.LogPosition)); prepares.Add(prepare); } } if (indexEntries.Count > 0) { if (_additionalCommitChecks && cacheLastEventNumber) { CheckStreamVersion(streamId, indexEntries[0].Version, commit); CheckDuplicateEvents(streamId, commit, indexEntries, prepares); } _tableIndex.AddEntries(commit.LogPosition, indexEntries); // atomically add a whole bulk of entries } if (eventNumber != EventNumber.Invalid) { if (eventNumber < 0) { throw new Exception(string.Format("EventNumber {0} is incorrect.", eventNumber)); } if (cacheLastEventNumber) { _backend.SetStreamLastEventNumber(streamId, eventNumber); } if (SystemStreams.IsMetastream(streamId)) { _backend.SetStreamMetadata(SystemStreams.OriginalStreamOf(streamId), null); // invalidate cached metadata } if (streamId == SystemStreams.SettingsStream) { _backend.SetSystemSettings(DeserializeSystemSettings(prepares[prepares.Count - 1].Data)); } } var newLastIndexedPosition = Math.Max(commit.LogPosition, lastIndexedPosition); if (_indexChk.Read() != lastIndexedPosition) { throw new Exception( "Concurrency error in ReadIndex.Commit: _lastCommitPosition was modified during Commit execution!"); } _indexChk.Write(newLastIndexedPosition); _indexChk.Flush(); if (!_indexRebuild) { for (int i = 0, n = indexEntries.Count; i < n; ++i) { _bus.Publish( new StorageMessage.EventCommitted( commit.LogPosition, new EventRecord(indexEntries[i].Version, prepares[i]), isTfEof && i == n - 1)); } } return(eventNumber); }