Summary
Three defects in how Chatter.SqlChangeFeed hands its options to the SqlServiceBroker transport. Found while rewriting the module README (branch docs/readme-overhaul); the README documents the current behavior.
1. WithMaxReceiveAttempts has no effect
SqlChangeFeedOptionsBuilder.WithMaxReceiveAttempts stores the value (Configuration/SqlChangeFeedOptionsBuilder.cs:203-207) and Build() copies it into ReceiverOptions.MaxReceiveAttempts (:226). But DependencyInjection/SqlChangeFeedExtensions.cs:71-78 calls AddQueueReceiver<ProcessChangeFeedCommand<T>>(...) with only errorQueuePath, transactionMode and deadLetterServicePath. maxReceiveAttempts is never passed, so the change feed receiver always uses the default of 10.
Fix: pass maxReceiveAttempts: options.ReceiverOptions.MaxReceiveAttempts, plus a test that pins it.
2. WithTransactionMode XML doc names the wrong default
The XML doc on WithTransactionMode (SqlChangeFeedOptionsBuilder.cs:193) says TransactionMode.ReceiveOnly is the default. The field default is TransactionMode.FullAtomicityViaInfrastructure (:26). Fix the doc (or the default, if the doc states the intended one).
3. Every AddSqlChangeFeed call re-registers SqlServiceBroker options; the last one wins
Each AddSqlChangeFeed<T> runs builder.AddSqlServiceBroker(...) with its own ServiceBrokerOptions (SqlChangeFeedExtensions.cs:71-73). AddSqlServiceBroker does builder.Services.AddSingleton(options) (src/Chatter.MessageBrokers.SqlServiceBroker/src/Chatter.MessageBrokers.SqlServiceBroker/DependencyInjection/Extensions.cs:60). The SqlServiceBroker receiver injects one SqlServiceBrokerOptions, so with two watched tables (or a change feed alongside a plain SqlServiceBroker registration) the last registration wins for every receiver. Per-feed settings such as connection string or conversation options can silently be replaced by another feed.
Impact
Configured values are silently ignored or overwritten. No data loss, but retry and deadletter timing, and possibly connection targets, differ from what the application configured.
Summary
Three defects in how
Chatter.SqlChangeFeedhands its options to the SqlServiceBroker transport. Found while rewriting the module README (branchdocs/readme-overhaul); the README documents the current behavior.1.
WithMaxReceiveAttemptshas no effectSqlChangeFeedOptionsBuilder.WithMaxReceiveAttemptsstores the value (Configuration/SqlChangeFeedOptionsBuilder.cs:203-207) andBuild()copies it intoReceiverOptions.MaxReceiveAttempts(:226). ButDependencyInjection/SqlChangeFeedExtensions.cs:71-78callsAddQueueReceiver<ProcessChangeFeedCommand<T>>(...)with onlyerrorQueuePath,transactionModeanddeadLetterServicePath.maxReceiveAttemptsis never passed, so the change feed receiver always uses the default of 10.Fix: pass
maxReceiveAttempts: options.ReceiverOptions.MaxReceiveAttempts, plus a test that pins it.2.
WithTransactionModeXML doc names the wrong defaultThe XML doc on
WithTransactionMode(SqlChangeFeedOptionsBuilder.cs:193) saysTransactionMode.ReceiveOnlyis the default. The field default isTransactionMode.FullAtomicityViaInfrastructure(:26). Fix the doc (or the default, if the doc states the intended one).3. Every
AddSqlChangeFeedcall re-registers SqlServiceBroker options; the last one winsEach
AddSqlChangeFeed<T>runsbuilder.AddSqlServiceBroker(...)with its ownServiceBrokerOptions(SqlChangeFeedExtensions.cs:71-73).AddSqlServiceBrokerdoesbuilder.Services.AddSingleton(options)(src/Chatter.MessageBrokers.SqlServiceBroker/src/Chatter.MessageBrokers.SqlServiceBroker/DependencyInjection/Extensions.cs:60). The SqlServiceBroker receiver injects oneSqlServiceBrokerOptions, so with two watched tables (or a change feed alongside a plain SqlServiceBroker registration) the last registration wins for every receiver. Per-feed settings such as connection string or conversation options can silently be replaced by another feed.Impact
Configured values are silently ignored or overwritten. No data loss, but retry and deadletter timing, and possibly connection targets, differ from what the application configured.