Changelog
All notable changes to ActualLab.Fusion are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
+HexNumber after version number is the commit hash of this version. It isn't included into the NuGet package version.
12.0.45+0ee956d2
Release date: 2026-01-29
Performance
- Simplified
RpcOutboundMessageby replacingWhenSerializedTask with synchronousSendHandlercallback; dependencies likeRpcOutboundCall.SendXxx,RpcStream.OnItem,OnBatch, andOnEndtransitioned fromTasktovoidreturn type reducing task allocations in the RPC send path, bringing ~20% performance improvement.
Tests
- Added comprehensive unit tests for
TaskCompletionHandlercovering various task states (completed, faulted, cancelled) and all handler variants (1, 2, 3 state objects)
Infrastructure
- Removed obsolete
docs.slnfile - Removed JetBrains.Annotations package reference
- Updated package versions: Blazorise 1.8.9 (used only in samples), Bullseye 6.1.0, and CliWrap 3.10.0 (used in Build.csproj)
- Updated analyzer packages: Moq.Analyzers, xunit.analyzers, Roslynator.Analyzers, Meziantou.Analyzer.
12.0.34+842f172c
Release date: 2026-01-28
Added
- Native little-endian serialization support in RPC serializers with optimized memory handling
TestServiceProviderTagto tag testServiceProviders and allow services to detect whether they're running in test containers- New
MemoryReaderandSpanWriterhelpers inActualLab.Core.IO.Internal
Performance
- Improved RPC performance dedicated path for little endian serialization and delegate caching
- Eliminated
RpcPeer.Sendindirection layer -RpcTransport.Sendnow handles error handling directly, reducing call stack depth and improving RPC throughput - Introduced
TaskCompletionHandler- a pooled helper for attaching completion callbacks to tasks. Each instance caches its delegate, and instances are pooled using thread-static pools to minimize allocations in hot paths like RPC transport error handling - Enabled
UseUnsafeAccessorsbuild configuration for .NET 8+ targets, improving internal reflection-based operations performance
Fixed
- Big endian system support: serialization, RPC, and all dependent code now correctly use
BinaryPrimitives.WriteInt32LittleEndianand similar methods to ensure consistent byte ordering across different CPU architectures
Tests
- Added
ResetClientServices()calls to all relevant Fusion and RPC tests - Refactored RPC test organization for better separation of concerns.
Documentation
- Added interactive BarChart component for performance benchmarks visualization
- Improved Mermaid flowchart styles and edge label rendering
- Updated Performance page with new benchmark visualizations
- Integrated CHANGELOG into the documentation website
- Updated coding style guide to clarify async method naming conventions
12.0.9+3e71b6ef
Release date: 2026-01-27
Breaking Changes
- New
v6serialization format:mempack6,msgpack6and their variants withcsuffix - All serialization formats below
v5are gone, usev11.5.1if you still need them WebSocketChannelis replaced byRpcWebSocketTransport- All authentication-related types from
ActualLab.Fusion.Serverare moved toActualLab.Fusion.Ext.*assemblies and changed namespace fromActualLab.Fusion.Server.AuthenticationtoActualLab.Fusion.Authentication, so referencingActualLab.Fusion.Serverassembly now doesn't "drag" the authentication-related types into your project (and that was the reason for this change).
Added
mempack6andmsgpack6serialization format versions; they offer a tiny improvement (2 bytes per call) over v5, and that's only because the zero-copy serialization in RPC transport layer made v5 a bit less efficient (it has to reserve 5 bytes for the message length, because it uses WriteVarUInt32 for length, so v6 changes length encoding to regular UInt32).RpcStream.BatchSizeproperty for controlling stream batching behavior (default: 64, range: 1..1024)- Real-time stock ticker demo added to
TodoAppsample.
Changed
- Consolidated buffer size properties in
RpcWebSocketTransport(renamedWriteFrameSizetoFrameSize) - Very significant changes in
ActualLab.Rpcinternals. In particular, oldRpcMessageis now represented byRpcOutboundMessageandRpcInboundMessagetypes, all serialization-related methods now accept different arguments, etc.
Performance
- Zero-copy serialization in RPC transport layer. Earlier an intermediate buffer (
ArgumentData) was used to serialize RPC call arguments and results. LaterRpcMessage(envelope) serializer was combining that data with other required pieces (method reference, call ID, etc.) in the final buffer. Now the intermediate buffer is eliminated, which significantly boosts RPC performance on large messages.Stream10Ktest shows almost 3x speed improvement on 10KB items. - Switched
WriteChannelOptionstoUnboundedChannelOptions - Buffer renewal and reuse logic was significantly improved as well.
Documentation
- New documentation website: https://fusion.actuallab.net/
11.4.7+3045fd2c
Release date: 2025-01-05
Added
- Updated EntityFrameworkCore and Npgsql versions to 10.0. There is currently no MySql EF Core provider for EF10, so if you want to use Fusion with MySql, the latest version that targets EF9 is 11.4.3; you can also add binding redirects for EF9 manually in your project.
RpcMethodAttributefor method-level RPC configurationv5serialization formats with proper polymorphicnullvalue support, includingjson5,njson5,msgpack5,msgpack5c,mempackc, andmempack5c.- New
IRpcMiddlewarestack replacingIRpcInboundCallPreprocessor RpcLocalExecutionModeenum and newRpcLocalExecutionMode.Constrained,RpcLocalExecutionMode.ConstrainedEntrymodesIOperationEventSourceinterface withOperation.AddEvent(...)overload for event sourcingIWorker.Runoverload withCancellationToken.- Much more robust RPC (re)routing logic in
RpcInterceptor,RpcRoutingCommandHandler, andRemoteComputeMethodFunction.
Changed
- Moved all
RpcXxxdelegates toRpcXxxOptionsmembers for clarity. E.g.,RpcOutboundCallOptions.RouterFactoryreplacesRpcCallRouterdelegate. - Renamed
RpcShardRoutingModetoRpcLocalExecutionMode - Renamed
RpcDefaultSessionInboundCallPreprocessortoRpcDefaultSessionReplacer - Simplified
RpcSerializationFormatResolver(the legacy resolver for the "unspecified" format is gone) - Improved
RpcServiceDefandRpcMethodDefconstructors - Improved
ComputedOptionscaching - Updated .NET SDK to version 10.0.101.
Performance
- Multiple improvements in inbound call processing performance, such as handcrafted server invokers for most frequent RPC system calls like
$sys.Ok WebSocketChannel.OptionsgotReadMode, which can beBufferedorUnbuffered. The newUnbufferedmode allows reading directly fromWebSocketbypassingChannelReader, it's used by default now.GetUnsafeinGenericInstanceCacheto eliminate some unnecessary type casts- Overall, v11.4.X is ~5-10% faster on RPC benchmarks.
Documentation
- Migrated Parts 01-13 from the old tutorial, though only parts 01-03 are truly edited at this point
- Added TOCs to videos on Fusion and ActualLab.Rpc
- Added GitHub workflow for deploying documentation to GitHub Pages: https://fusion.actuallab.net/
- Documentation is a work in progress, and you're welcome to contribute!
Fixed
- Multiple issues related to RPC rerouting
RpcCommandHandlerrepeatedly sending commands to the server- A new bug in
FrameDelayers.MustDelaymethod (introduced in late v11.3.X), which effectively disabled RPC frame delaying - Use of incorrect Handshake index on some reconnection attempts – the issue was rare, but once it happened, it was blocking RPC reconnects for ~5 min.
Task.Resultusage is replaced with.GetAwaiter().GetResult()everywhere (it's faster and safer)$csys.Invalidatecalls (remote invalidation notifications) now triggerComputed.Invalidate(immediately: true)call rather than justComputed.Invalidate(), which eliminates double delay for RPC compute methods that use invalidation delay- Various minor fixes.
Tests
WebTestHelpers.GetUnusedLocalUrihelper method inActualLab.TestingCapturingLoggerandCapturingLoggerProviderinActualLab.Testing- Improved cancellation and timeout handling in RPC tests
- Added benchmark tests for
Task.ResultvsTask.GetAwaiter().GetResult() - Added
CapturingLoggerunit test
11.0.15+ec823882
Release date: 2025-11-05
Changed
- Returned back
IMutableState.Valuesetters (they were removed in v11.0.8) - Added "Must" prefix to
RpcDefaultCallTracer.TraceInboundandTraceOutboundflags for consistency with bool field naming rules.
Fixed
- Adjusted
RpcDefaultDelegates.CallTracerFactoryto enable full tracing for server (RuntimeInfo.IsServer), and no tracing for the client.
Changed in Samples
- TodoApp: Updated Aspire SDK to version 9.5.2 and centralized its version configuration
- TodoApp: Temporarily disabled Microsoft Account authentication (the current credentials are expired).
11.0.8+1fd1d61afb
Release date: 2025-11-02
Breaking Changes
- Revamped
RpcServiceBuilderAPI. Its newInjectmethod "injects" a service described byRpcServiceBuilderintoIServiceCollection RpcBuilderandFusionBuilder'sAddXxxmethods now rely onRpcServiceBuilder.InjectIMutableStateandMutableStatelostValuesetter; useSet(...)methods to set it. Invalidation path tracking is the reason of this change: new.Set(...)overloads use[CallerFilePath],[CallerMemberName], and[CallerLineNumber]to propagate the origin of change to the invalidation logic, and there is no way to achieve the same with.Valueproperty. I may end up returning it with[Obsolete]attribute though.- Renamed
IHasIsDisposedtoIHasDisposeStatus - Removed
RpcServiceMode.DistributedPairmode and related logic (Distributedmode offers more anyway) - Removed
RpcSwitchInterceptor;
Added
- Quite useful
ComputedOptions.ConsolidationDelayand related APIs (see Releases chat on https://voxt.ai/chat/s-1KCdcYy9z2-uJVPKZsbEo for details) - Invalidation path tracking:
InvalidationSource,Invalidation.TrackingMode, and other changes Computed.ToString(InvalidationSourceFormat)isComputed.ToString(), but with invalidation path infoFusionMonitoris now capable of gathering invalidation path statistics.
Changed
- More readable output of
MethodInfo.ToShortString()is now used to dump compute service methods
Fixed
- Another issue in
RpcCallTracker.TryReconnectthat may block stateful reconnect - A couple places in Fusion RPC stack where
Computed.Invalidate(immediately: false)was used instead ofComputed.Invalidate(immediately: true) - Incorrect use of
Sampler-s inFusionMonitor: missing "not" was making it to sample where it had to skip, and vice versa 😦 That's why statistics was typically 7x exaggerated there (the probability of loggingEveryNth(8)is 7/8, i.e. close to 1, but it was reporting it as 1/8).
10.6.38+254f4ef775
Release date: 2025-10-28
Changed
- Breaking: Replaced
RpcCallRouteOverridewithRpcCallOptions, expect more changes in this area - Removed
RpcNonRoutingInterceptorand related infrastructure;RpcRoutingInterceptordoes a bit more and equally fast - Updated CODING_STYLE.md
Fixed
RpcCallTracker.TryReconnect-IncreasingSeqCompressor.Serializewas getting a potentially misordered sequence making fast (stateful) reconnect impossible, though stateless reconnect still worked in these cases- Renamed
CompleteAsyncclass toCompletionto correct a previous wrong rename RpcRoutingInterceptornow properly reroutes local calls as well
Tests
- Added new MeshRpc tests; will be extending them in the near future
- Refactored
FusionTestBasedescendants to move DI of test-specific services to specific tests - Reorganized DB model classes under
DbModelnamespace in tests
10.6.18+af2a52320f
Fixed
- ActualLab.Generators now add #if-s suppressing [UnconditionalSuppressMessage] and [ModuleInitializer] for .NET Framework 4.7.2 and .NET Standard 2.0.
10.6.16+d0431715b7
Release date: 2025-10-27
Changed
- Renamed
ChannelReadModetoWebSocketChannelReadMode - Removed
IChannelWithReadMode, updatedWebSocketChannelto implementIAsyncEnumerabledirectly.
Performance
- Adjusted
BoundedChannelOptionsforReadChannel(120→100) andWriteChannel(120→500) inWebSocketChannelto optimize buffering behavior
Infrastructure
- Added
UnbufferedPushSequence<T>for unbuffered async data streaming - Added
[UnsafeAccessor]-based helpers forAsyncTaskMethodBuilder<T>inAsyncTaskMethodBuilderExt.
Documentation
- Replaced
.instructions.mdwith updatedAGENTS.mdandCODING_STYLE.md
10.6.4
Release date: 2025-10-25
Breaking Changes
- Breaking: Removed
FusionDefaultsandFusionModetypes. UseRuntimeInfo.IsServer,ComputedRegistry.Settings, andTimeouts.Settingsinstead. - Breaking: Removed
RpcModetype. UseRuntimeInfo.IsServerinstead. - Breaking:
ComputedRegistry.Instanceis gone, the whole class is now static - Breaking:
TimeoutsandIGenericTimeoutHandlerare moved toActualLab.Timenamespace. - Breaking: reworked
OperationEventAPI enabling events with quantized delay. Such events have Uuid, which includes its DelayUntil value, and have quantized DelayUntil. So you can use them to implement reliable throttling for such activities as post-change indexing or AI processing - Breaking:
IHasDelayUntilinterface is removed, and thus its support inOperationEventhandling logic. - Breaking: .NET Framework 4.7.1 target is replaced with .NET Framework 4.7.2
Added
- .NET 10 RC2 support
allowInconsistentflag inComputed/AnyState/ComputedSource.Use()and.UseUntyped()methods. Get the value even if it's inconsistent; when called inside a compute method, instantly invalidates the newly created computed if an inconsistent dependency gets captured.bool Operation.MustStoreproperty allowing to disable the operation log entry creation; this is useful when you're sure you don't need distributed invalidation for the current operation - e.g., you know that only the local machine is responsible for exposing modified data. WhenMustStoreisfalse, aDbEvent(inProcessedstate) is created instead of anDbOperationentry to verify commit in case of commit failure.OperationScope.CompletionHandlersallowing to register operation completion handler right inside the command handlerRpcCallRouteOverridetype allowing to override outgoing RPC call routing (e.g., set destination peer)Moment.Floor,Moment.Ceiling,Moment.Round, andMoment.Convertmethods;Moment.Ceilingis used to quantize delayed events.
Changed
- Moved command routing logic to
RpcRoutingCommandHandler - Renamed
ComputeServiceCommandCompletionInvalidatortoInvalidatingCommandCompletionHandler - Improvements in
RetryPolicy/IRetryPolicy, including exception filters ArgumentListG*<...>is now IL/AOT-trimmable viaArgumentList.AllowGenericsfeature switch
Fixed
- Native AOT support in .NET 10
- A bug in
ComputeRegistrythat could causeComputedRegistryto expose a wrongComputeddue to a race betweenComputedRegistry.GetandComputedRegistry.Register. IfGetgets paused between a moment it read a handle and resolved itsTarget, the handle with the sameIntPtrvalue might get re-allocated. This is extremely rare, but nevertheless, we saw this happening in production. - The identical bug in
RpcObjectTracker(it's used to trackRpcStream-s). IDelegatingCommandnow "implements"IOutermostCommand(it was supposed to, but didn't)StatefulComponent.StateChangedhandler now suppressesExecutionContextflow- Bug in
RpcSystemCallSender.OkbreakingRpcStreamserialization in call results - Bug in
RpcStreamenumerator - Proper framework version check in
CpuTimestampfor .NET 10 WASM - Removed
Microsoft.Extensions.Httpreference fromActualLab.Rpc
Performance
WebSocketChannel.ReadModeand corresponding option enabling unbuffered reads without use ofReader(which is backed by its ownChannel). This option alone improves RPC performance by 5%.- Use
CancellationToken.NoneinWebSocketoperations inWebSocketChannelwithout sacrificing cancellation support (WebSocketis properly disconnected on cancellation now) RpcStreamnow usesMemory<byte>instead ofbyte[]for its buffer.- Use of
Unsafe.As<T>(x)instead of(T)xin a few key places. - Improvements in key Fusion components, including
ComputedRegistry,Computed, andComputedState, - Improvements in Blazor components, including
StatefulComponentBaseandComputedStateComponentBase - Improvements in infrastructure components, including
ArgumentList,FixedArray,AsyncLock, andAsyncLockSet.
Infrastructure
- Added
WeakReferenceSlim(currently unused) - Added
TaskExt.NeverEnding(CancellationToken)helper - Added
FixedTimerSetandConcurrentFixedTimerSet - Improved
CancellationTokenSource-based timeout handling code across the board - Improved tests.
Documentation
- Added
.instructions.md(like AGENTS.md) for agent rules - Finalized Part01, added examples for
Computed<T>.When()andChanges()methods.
Earlier Versions
For changes in earlier versions, please refer to the git commit history or see "Fusion/🎉Releases" on Voxt.ai: https://voxt.ai/chat/s-1KCdcYy9z2-uJVPKZsbEo
