Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ never breaks for consumers. `test/Fitz.PackageConsumer` asserts this on every CI

## [Unreleased]

## [1.4.2] - 2026-09-21

### Fixed

- **`Cntryl.Fitz.Abstractions`:** expose `FitzErrorCodes.StreamConcurrencyConflict` (2001) and
`FitzErrorCodes.StreamSessionAlreadyActive` (2002), so callers can classify the broker's
structured stream write-contention responses without matching error text.

## [1.4.1] - 2026-09-21

### Added
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<PropertyGroup Condition="'$(IsPackable)' != 'false'">
<!-- Assembly identity stays fixed permanently; release only by advancing PackageVersion. -->
<PackageVersion>1.4.1</PackageVersion>
<PackageVersion>1.4.2</PackageVersion>
<Version>$(PackageVersion)</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<InformationalVersion>$(PackageVersion)</InformationalVersion>
Expand All @@ -26,7 +26,7 @@
<DebugType>portable</DebugType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>fitz distributed-systems kv queue rpc lease notice stream schedule</PackageTags>
<PackageReleaseNotes>Adds bounded read-only primary-key paging for Cntryl.Fitz.Extensions directories. See CHANGELOG.md.</PackageReleaseNotes>
<PackageReleaseNotes>Exposes named stream write-contention error codes for broker codes 2001 and 2002. See CHANGELOG.md.</PackageReleaseNotes>
<PackageOutputPath>$(MSBuildThisFileDirectory)artifacts\packages</PackageOutputPath>
</PropertyGroup>

Expand Down
14 changes: 9 additions & 5 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ catch (RpcException ex) when (ex.DomainCode == FitzErrorCodes.RpcTimeout)
}
```

`FitzErrorCodes` lists every code the broker defines. Transport and lifecycle problems surface
as `ConnectionException`, `AuthenticationException`, `RequestTimeoutException`,
`RequestQueueFullException`, or `ProtocolException`.
`FitzErrorCodes` exposes named constants for the broker codes the SDK classifies. Other
structured domain codes remain available on an exception's `DomainCode`; never match error text.
Transport and lifecycle problems surface as `ConnectionException`, `AuthenticationException`,
`RequestTimeoutException`, `RequestQueueFullException`, or `ProtocolException`.

Two exceptions sit outside that hierarchy and derive from `Exception` directly:
`SubscriptionBackpressureException` and `AsyncHandlerOverflowException`. They are declared in
Expand Down Expand Up @@ -313,8 +314,11 @@ await session.CommitAsync(ct);
```

`AppendAsync` takes the offset you expect to write at; a mismatch is an optimistic-concurrency
failure, reported as `StreamException` with `DomainCode` 2001. Classify on that code, never on
the message.
failure, reported as `StreamException` with `DomainCode ==
FitzErrorCodes.StreamConcurrencyConflict`. `BeginAsync` reports
`FitzErrorCodes.StreamSessionAlreadyActive` when an append session currently owns the resource
stream. These are distinct structured write-contention conditions; classify on the
code, never on the message.

Use `ReadPageAsync` when you need to see what the broker withheld: a `StreamReadPage` contains
`StreamReadItem`s whose `Kind` distinguishes delivered records from filtered ones, so gaps in
Expand Down
11 changes: 11 additions & 0 deletions src/Fitz.Abstractions/FitzErrorCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public static class FitzErrorCodes
/// </summary>
public const uint KvSubscriptionLimit = 1013;

/// <summary>
/// An append operation found that the stream's committed position no longer matches its
/// expected position.
/// </summary>
public const uint StreamConcurrencyConflict = 2001;

/// <summary>
/// An append session is already active for the resource stream.
/// </summary>
public const uint StreamSessionAlreadyActive = 2002;

/// <summary>
/// The stream subscription selector was malformed or unsupported.
/// </summary>
Expand Down
16 changes: 13 additions & 3 deletions test/Fitz.Core.Tests/Unit/StreamErrorEnvelopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ namespace Cntryl.Fitz.Core.Tests.Unit;

public sealed class StreamErrorEnvelopeTests
{
[Fact]
public void ShouldMatchWireProtocolGivenStreamWriteContentionCodesWhenValidated()
{
// Arrange
// Act
// Assert
Assert.Equal(2001u, FitzErrorCodes.StreamConcurrencyConflict);
Assert.Equal(2002u, FitzErrorCodes.StreamSessionAlreadyActive);
}

[Theory]
[InlineData("APPEND", 2001u, "unrelated wording")]
[InlineData("COMMIT", 2001u, "unrelated wording")]
[InlineData("APPEND", 2002u, "concurrency conflict")]
[InlineData("APPEND", FitzErrorCodes.StreamConcurrencyConflict, "unrelated wording")]
[InlineData("COMMIT", FitzErrorCodes.StreamConcurrencyConflict, "unrelated wording")]
[InlineData("BEGIN", FitzErrorCodes.StreamSessionAlreadyActive, "unrelated wording")]
[InlineData("COMMIT", 2012u, "backend unavailable")]
[InlineData("BEGIN", 2003u, "session unavailable")]
[InlineData("ROLLBACK", 2003u, "session unavailable")]
Expand Down
10 changes: 10 additions & 0 deletions test/Fitz.PackageConsumer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@
throw new InvalidOperationException("RPC error code constants must cover the canonical 6001-6013 range.");
}

uint[] streamWriteContentionCodes =
[
FitzErrorCodes.StreamConcurrencyConflict,
FitzErrorCodes.StreamSessionAlreadyActive,
];
if (!streamWriteContentionCodes.SequenceEqual(Enumerable.Range(2001, 2).Select(static code => (uint)code)))
{
throw new InvalidOperationException("Stream write-contention error code constants must cover the canonical 2001-2002 range.");
}


static async Task CompilePreviewApiAsync(
INoticeClient notice,
Expand Down
Loading