You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Avoid unnecessary allocations in ParseInfo / ParseCategorizedInfo in RedisDatabase #669
Turning a public class into a struct swaps reference semantics for value semantics on a type consumers already hold: null checks and InfoDetail x = null stop compiling, reference equality changes meaning, and it's a binary break on top of the source break. So that one belongs in v14, alongside #663 and the ServerIteratorFactory removal — not here.
Worth weighing on its own merits too, separately from the versioning. InfoDetail carries three strings, so as a struct it's 24 bytes copied on every pass rather than 8 for the reference, and it's handed out as an array from an admin-path call that just parsed a multi-kilobyte INFO payload. The parsing dominates either way. I'm not against it, but I'd want a reason beyond symmetry before spending a breaking change on it.
The rest is fine for v13.5
Moving the parse into InfoDetail, dropping the intermediate array, and shrinking the surface are all internal — nothing observable to consumers. Same for GetInfoSectionAsync(string section): purely additive, and genuinely useful given INFO memory alone is a fraction of the full payload we currently parse and discard.
Your sequencing is right, and I checked: SpanExtensions.cs currently holds both EnumerateLines and ToFastArray(ReadOnlySpan<T>). #664 moves the latter to GenericsExtensions, which leaves EnumerateLines as the only occupant — so once this issue relocates it into InfoDetail, the file goes away entirely. Clean chain.
I've put this on v13.5 with the struct question carved out; open a separate issue for that against v14 whenever you want to make the case.
Recording @LeaFrock's case for InfoDetail as a readonly struct, from the discussion on #670, so it isn't lost when the v14 conversation happens.
His two arguments:
Memory layout. For an array of N entries, a struct holding three string references is 24N bytes, against roughly 48N for a class once you count the reference plus object header. That compaction is real, and better than the "symmetry" reasoning I dismissed earlier in this issue — I underweighted it.
Semantics. The data originates from the server and is never mutated, so a read-only value type states the intent that a class with get-only properties only implies.
The counterweights, for whoever picks this up: it stays a source-and-binary breaking change on a type exposed through IRedisDatabase.GetInfoCategorizedAsync() — null checks against InfoDetail stop compiling and reference equality changes meaning. And 24 bytes get copied on every element access, so code doing response.Single(x => x.Key == ...) over a large INFO payload pays for it.
Not a decision for v13.5 either way. Worth its own issue against v14.0 — Breaking Changes when someone wants to make the call.
Activity
imperugo commentedon Aug 7, 2026
Good issue — the core of it is straightforward and I'd take it. One part needs splitting off, though.
The struct question is a breaking change
InfoDetailispublic, and it's exposed throughIRedisDatabase:Turning a public class into a struct swaps reference semantics for value semantics on a type consumers already hold: null checks and
InfoDetail x = nullstop compiling, reference equality changes meaning, and it's a binary break on top of the source break. So that one belongs in v14, alongside #663 and theServerIteratorFactoryremoval — not here.Worth weighing on its own merits too, separately from the versioning.
InfoDetailcarries three strings, so as a struct it's 24 bytes copied on every pass rather than 8 for the reference, and it's handed out as an array from an admin-path call that just parsed a multi-kilobyteINFOpayload. The parsing dominates either way. I'm not against it, but I'd want a reason beyond symmetry before spending a breaking change on it.The rest is fine for v13.5
Moving the parse into
InfoDetail, dropping the intermediate array, and shrinking the surface are all internal — nothing observable to consumers. Same forGetInfoSectionAsync(string section): purely additive, and genuinely useful givenINFO memoryalone is a fraction of the full payload we currently parse and discard.Your sequencing is right, and I checked:
SpanExtensions.cscurrently holds bothEnumerateLinesandToFastArray(ReadOnlySpan<T>). #664 moves the latter toGenericsExtensions, which leavesEnumerateLinesas the only occupant — so once this issue relocates it intoInfoDetail, the file goes away entirely. Clean chain.I've put this on v13.5 with the struct question carved out; open a separate issue for that against v14 whenever you want to make the case.
imperugo commentedon Aug 14, 2026
Recording @LeaFrock's case for
InfoDetailas areadonly struct, from the discussion on #670, so it isn't lost when the v14 conversation happens.His two arguments:
The counterweights, for whoever picks this up: it stays a source-and-binary breaking change on a type exposed through
IRedisDatabase.GetInfoCategorizedAsync()— null checks againstInfoDetailstop compiling and reference equality changes meaning. And 24 bytes get copied on every element access, so code doingresponse.Single(x => x.Key == ...)over a largeINFOpayload pays for it.Not a decision for v13.5 either way. Worth its own issue against v14.0 — Breaking Changes when someone wants to make the call.