When using ReadStreamEventsForwardAsync, in what cases will a ResolvedEvent have a null OriginalPosition?

I’m running into an issue when using ReadStreamEventsForwardAsync to read events from a single stream. I’m trying to use the EventStore Position as a checkpoint that I pass around on my message envelope for when I need to implement idempotency somewhere. The problem is that the OriginalPosition on the ResolvedEvent is coming back as null. Please see the debugger screenshot below. I couldn’t find it in the documentation, but in what cases will the OriginalPosition be null and how should this be interpreted? Also, is it appropriate to be using the Position for implementing idempotency in a read model sql database? Will this position ever change within a specific instance of EventStore or a cluster?

If you know you’re on a particular stream, why not use the streamid + eventnumber as your checkpoint instead?

Because where I’m actually using the checkpoint, I don’t know what particular stream it came from.

I am unsure if position is ever set on reading a stream. Its normally used with reading from all, if so I would have to check the edge conditions on it. There are some odd edge conditions to watch for (ex link exists but linked to event doesn’t and such). I can’t come up with one off the top of my head.

"Also, is it appropriate to be using the Position for implementing idempotency in a read model sql database? "

You can yes and thats what people normally do with fromAll. Lets imagine that you last processed 15,28882 and its been deleted. The query will actually give the next event higher. This also works in handling odd ball cases involving interleaved transactions etc though we will likely be removing these.

" Will this position ever change within a specific instance of EventStore or a cluster?"

No this is a key concept. Positions do not change in a cluster (otherwise fromAll would never have any hope of working :))




Thanks, that’s reassuring. I’ll need to rethink my implementation a little bit. I’m not sure that I need to be plugging in my checkpoint at that specific point in code. I don’t think I’ll actually need it when rebuilding my aggregates, so I’ll probably just ignore it there.


It may be reasonable for us to provide it in that case. Did you ever receive it on any request? My guess is we just don’t send it back for from stream

I never used the Position before when reading from an individual stream, so I can’t really say if it ever comes back or not in that case. The reason I started including it was to maintain consistency on my message envelope header format between my write side and my read side to simply the interfaces to some of my message handlers. I basically wrap my messages like so when busing them around between handlers: IEnvelope and my envelope header contains the following fields:

    public Guid MessageId { get; private set; }
    public TimeSpan Delay { get; private set; }
    public DateTime TimeStampUtc { get; private set; }
    public Guid UserId { get; private set; }
    public Guid CorrelationId { get; private set; }
    public Guid CausationId { get; private set; }
    public int Version { get; private set; }
    public ICheckpoint Checkpoint { get; private set; }

I translate these back and forth between my message representation and EventStore’s EventData and RecordedEvent representations using a converter class. All messages (events, commands, etc.) are wrapped with the same header event though some of the metadata isn’t used by certain message types. Like I said, I can probably just ignore the checkpoint on the write side of things since I am not really using it there…yet.