Describe the bug
We are encountering periodic warning log entries with the message "Failed to get end of stream" in applications using SQL-Server-backed Eventuous subscriptions. After investigation, SqlSubscriptionBase.GetSubscriptionEndOfStream has three bugs, all causing the periodic "Failed to get end of stream" warning:
- Swapped switch arms:
SubscriptionKind.All executes GetEndOfStream (stream-scoped MAX(stream_position)) instead of GetEndOfAll (global MAX(global_position)), and vice versa.
- Type mismatch on non-empty tables:
reader.GetInt64(0) is used to read the result of MAX(stream_position) / MAX(global_position). ADO.NET's typed getter is strict — if the database driver returns the column as int (32-bit), GetInt64 throws an InvalidCastException. This happens even when events are present.
- No
DBNull guard on empty tables: MAX() returns NULL when the table is empty, causing GetInt64(0) to throw InvalidCastException regardless of the type issue above.
The warning appears periodically because GetSubscriptionEndOfStream is exposed via IMeasuredSubscription.GetMeasure() and called repeatedly by the subscription diagnostics/metrics system to measure subscription lag.
Additional context
The fix in GetSubscriptionEndOfStream:
async ValueTask<EndOfStream> GetSubscriptionEndOfStream(CancellationToken cancellationToken) {
try {
await using var connection = await OpenConnection(cancellationToken).NoContext();
await using var cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = Kind switch {
SubscriptionKind.All => GetEndOfAll,
SubscriptionKind.Stream => GetEndOfStream
};
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).NoContext();
var position = await reader.ReadAsync(cancellationToken).NoContext() && reader[0] is not DBNull
? Convert.ToInt64(reader[0])
: 0;
return new(SubscriptionId, (ulong)position, DateTime.UtcNow);
} catch (Exception e) {
Log.WarnLog?.Log(e, "Failed to get end of stream");
return EndOfStream.Invalid;
}
}
Describe the bug
We are encountering periodic warning log entries with the message
"Failed to get end of stream"in applications using SQL-Server-backed Eventuous subscriptions. After investigation,SqlSubscriptionBase.GetSubscriptionEndOfStreamhas three bugs, all causing the periodic"Failed to get end of stream"warning:SubscriptionKind.AllexecutesGetEndOfStream(stream-scopedMAX(stream_position)) instead ofGetEndOfAll(globalMAX(global_position)), and vice versa.reader.GetInt64(0)is used to read the result ofMAX(stream_position)/MAX(global_position). ADO.NET's typed getter is strict — if the database driver returns the column asint(32-bit),GetInt64throws anInvalidCastException. This happens even when events are present.DBNullguard on empty tables:MAX()returnsNULLwhen the table is empty, causingGetInt64(0)to throwInvalidCastExceptionregardless of the type issue above.The warning appears periodically because
GetSubscriptionEndOfStreamis exposed viaIMeasuredSubscription.GetMeasure()and called repeatedly by the subscription diagnostics/metrics system to measure subscription lag.Additional context
The fix in
GetSubscriptionEndOfStream: