Add SER308: flag the library's own Wait/WaitAll/TryWait helpers (#3180)
* Add SER308: the library's own Wait helpers block a thread too Wait, WaitAll and TryWait are what SER307 discourages everywhere else, reached through the API the library itself offers for it: the calling thread is held for the round-trip while the reply needs a thread of its own. Shipping SER307 while providing a blessed way to do the same thing was only ever half a position. An analyzer rule rather than [Obsolete], and the reason is entirely about how it is turned off. [Obsolete] reports CS0618, which every obsoletion from every source shares, so a consumer wanting to silence *this* has to silence *all* of them - including deprecations in their own code and in unrelated packages. That was the review feedback, and it is right. ObsoleteAttribute.DiagnosticId exists for exactly this and would be the natural answer, but it is net5+ and this library still targets netstandard2.0: it would give a granular ID on some target frameworks and CS0618 on others. A hybrid was considered and rejected - the attribute would need #if on the interface *and* on ConnectionMultiplexer's own members, since marking only the interface does not warn through the class, and on modern targets both mechanisms would report at the same location. [Experimental] is granular but would be saying something untrue; these APIs are long-standing, not preview. Recorded in the descriptor as worth revisiting rather than settled: if netstandard2.0 and net4x are ever dropped, the attribute becomes strictly better and this rule can retire in its favour. Two unrelated interfaces declare these - IRedisAsync for database, server and subscriber calls, and IConnectionMultiplexer for its own - so both are gathered. Matching goes through FindImplementationForInterfaceMember as well as directly, because ConnectionMultiplexer is what Connect returns, making `conn.Wait(task)` the common shape with the class as its containing type. Testing one interface would have left half the surface unguarded, which is how the first attempt at this missed the multiplexer entirely. * Suppress SER308 where this repo blocks deliberately Two kinds, and the distinction is in the comments rather than left to the reader. Most are decorator layers - KeyPrefixed, RedisBase, the Retry* and MultiGroup* wrappers, the test fixture - which must implement the interfaces in full and simply forward; forwarding a flagged member is not using it, and the implementation cannot be dropped while the interface declares it. The exception is CursorEnumerable's synchronous scan path, which is the real thing: it is reached only from a caller who asked for the sync API, and TryWait is what applies the configured timeout to it. Test call sites are suppressed rather than rewritten to await, deliberately: the Wait helpers apply the multiplexer's timeout, which a bare await does not, so rewriting would quietly change what those tests exercise.
M
Marc Gravell committed
9201860e1cfb9f174e403437a95e63eeaeda71d6
Parent: 89cf0c6
Committed by GitHub <noreply@github.com>
on 8/18/2026, 3:22:40 PM