Search code examples
c#eventsevent-sourcingsoft-deleteeventstoredb

Event Store cannot write to soft deleted streams


To clearify: This question is about Greg Young's Event Store.

I tried to soft delete a stream which contained 2 events:

var slice = con.ReadStreamEventsBackwardAsync(streamName, 0, 1, resolveLinkTos: true).Result;
es.DeleteStreamAsync(streamName, slice.LastEventNumber, hardDelete: false).Wait();

This call was successful and investigating the store revealed a new metadata event. This event was of type $metadata and contained:

{
  "$tb": 9223372036854775807
}

$tb stands for "truncate before" and is described in Deleting streams and events. The documentation says:

When you delete a stream, its TruncateBefore or $tb is set to the streams current last event number.

Which (as you can see in the json above) is not the case. Truncate before is set to long.MaxVaue. Although this seems to be bad behaviour it is not the actual problem. The issue is that I cannot write to the stream anymore. Invoking the following snipped completes successfully but does not append any event to the stream:

await es.AppendToStreamAsync(persistenceId, expectedVersion < 0 ? ExpectedVersion.NoStream : expectedVersion, events);

In the snipped above, expectedVersion is set to -1. The metadata of the soft deleted stream says:

Stream is deleted: False
Meta Stream version: 0
Truncate before: 9223372036854775807
Max count:
Max age:

And reading the slice from the last event of the stream reveales:

Last Event number: 1
Next Event number: -1
Status: StreamNotFound

Has anybody encountered the same issue and might have found a solution which allows continuing appending events to deleted streams?


Solution

  • I received some valuable information through my Pull Request. First of all, the Event Store is behaving correctly even if it does not behave as described in the documentation at the current point in time.

    The reason why we couldn't write to the stream was because EventStore recognized our event as a duplicate and ignored it. A duplicate is an event with the same id as a previously received event. The event id is client generated and is neither the stream name nor the event index.

    That means you have to be careful when generating an event id. Make sure your event ids are unique!

    Our problem was that our event ids were Guids deterministically generated from the stream name and the event count. The event count is the number of events in the stream. A soft deleted stream contains 0 events which caused the id of the first event after the soft deletion to have the same id as the first event before deleting the stream (and the second event after deletion generated the same is as the second event before deletion, and so on).

    Our solution was to calculate the event id from StreamEventSlice.LastEventNumber (which is exposed by the ClientApi) and not from the number of events in the stream.