-
Notifications
You must be signed in to change notification settings - Fork 27
Description
Describe the bug
Description
In Microsoft SQL Sever, the default connection options can be configured. One of the options is to Set NOCOUNT at the server level via SQL Server Management Studio or sp_configure 'user options'. When NOCOUNT is ON at the server level, after a saga message is handled, an exception is logged.
Optimistic concurrency violation when trying to save saga <saga name> <guid>. Expected version 1.
Expected behavior
The Saga Data should be updated, and the message handler completes. No matter what the SQL Server's NO COUNT configuration is.
Actual behavior
The message fails and ends up in the Error queue.
Versions
Seen in NServiceBus.Persistence.Sql version 7.0.6
Steps to reproduce
Set NOCOUNT OFF at the MS SQL Server level configuration, and run a saga using MS SQL persistence.
Relevant log output
Additional Information
Workarounds
Set the SQL Server NOCOUNT option to OFF
https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-user-options-server-configuration-option?view=sql-server-ver17
Possible solutions
This appears to be coming from:
| var affected = await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); |
The code is checking for a Row Count of 1 as returned from ExecuteNonQueryAsync method which uses the count returned by SQL server.
The code is executing a statement as defined here:
| update {tableName} |
update {tableName}
set
Data = @Data,
PersistenceVersion = @PersistenceVersion,
SagaTypeVersion = @SagaTypeVersion,
Concurrency = @Concurrency + 1{correlationSet}
where
Id = @Id and Concurrency = @Concurrency
I think this could be made to work when NOCOUNT is ON, by having the statement deliberately select the rowcount:
update {tableName}
set
Data = @Data,
PersistenceVersion = @PersistenceVersion,
SagaTypeVersion = @SagaTypeVersion,
Concurrency = @Concurrency + 1{correlationSet}
where
Id = @Id and Concurrency = @Concurrency;
select @@ROWCOUNT
There would also need to be a change to SagaPersister_Update.cs to use ExecuteScalarAsync instead of ExecuteNonQueryAsync. Though this would affect the other Dialects as well:
Alternatively, add a NOCOUNT OFF to the statement beforehand. Though I don't know what affect this would have in subsequent statements being executed.
SET NOCOUNT OFF;
update {tableName}
set
Data = @Data,
PersistenceVersion = @PersistenceVersion,
SagaTypeVersion = @SagaTypeVersion,
Concurrency = @Concurrency + 1{correlationSet}
where
Id = @Id and Concurrency = @Concurrency