diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 524e5fd4bfd..71ee73ab0cc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,27 @@ +#### 1.4.20 May 12 2021 #### +**Maintenance Release for Akka.NET 1.4** + +Akka.NET v1.4.20 is a minor release that includes some bug fixes and improvements to Akka.NET. + +* [Akka: Fixed some `internal-dispatcher` usages ](https://github.com/akkadotnet/akka.net/pull/4995) +* [Akka: Remove restrictions required by netstandard 1.x but available in 2.0 ](https://github.com/akkadotnet/akka.net/pull/3790) +* [Akka: Prevent loggers to throw `FormatException` and show a friendly message instead. ](https://github.com/akkadotnet/akka.net/pull/4998) +* [Akka.Persistence.Sql.Common: Some persistent actors are stuck with `RecoveryTimedOutException` after circuit breaker opens](https://github.com/akkadotnet/akka.net/issues/4265) +* [Akka.Persistence.Sql.Common: marking Akka.Persistence.Sql.Common as `beta` for v1.4.20](https://github.com/akkadotnet/akka.net/pull/5006) +* [Akka.Remote: `Akka.Remote.Serialization.PrimitiveSerializers` needs cross platform compatibility](https://github.com/akkadotnet/akka.net/issues/4986) +* [Akka.Streams: Fixed `QueueSource.PostStop` and added a missing test case](https://github.com/akkadotnet/akka.net/pull/4991) +* [Akka.Cluster.Sharding: Reduce sharding warnings when there are no buffered messages](https://github.com/akkadotnet/akka.net/pull/5003) + +To see the [full set of fixes in Akka.NET v1.4.20, please see the milestone on Github](https://github.com/akkadotnet/akka.net/milestone/50). + +| COMMITS | LOC+ | LOC- | AUTHOR | +| --- | --- | --- | --- | +| 4 | 677 | 316 | Gregorius Soedharmo | +| 3 | 3 | 3 | dependabot[bot] | +| 3 | 101 | 44 | Ismael Hamed | +| 2 | 5 | 0 | Aaron Stannard | +| 1 | 625 | 675 | Matthew Heaton | + #### 1.4.19 April 28 2021 #### **Maintenance Release for Akka.NET 1.4** diff --git a/src/common.props b/src/common.props index bcf07da8e3f..809e3c7a451 100644 --- a/src/common.props +++ b/src/common.props @@ -14,13 +14,13 @@ 0.10.1 13.0.1 2.0.1 - 3.15.8 + 3.16.0 netcoreapp3.1 net5.0 net471 netstandard2.0 5.10.3 - 2.15.2 + 2.15.3 2.0.3 4.7.0 akka;actors;actor model;Akka;concurrency diff --git a/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/Akka.Cluster.Sharding.Tests.MultiNode.csproj b/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/Akka.Cluster.Sharding.Tests.MultiNode.csproj index 5e3524af359..377aa9d05d8 100644 --- a/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/Akka.Cluster.Sharding.Tests.MultiNode.csproj +++ b/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/Akka.Cluster.Sharding.Tests.MultiNode.csproj @@ -25,14 +25,6 @@ - - $(DefineConstants); - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/AsyncWriteProxyEx.cs b/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/AsyncWriteProxyEx.cs index c7faf070f9e..b84c623aff7 100644 --- a/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/AsyncWriteProxyEx.cs +++ b/src/contrib/cluster/Akka.Cluster.Sharding.Tests.MultiNode/AsyncWriteProxyEx.cs @@ -43,7 +43,6 @@ public AsyncReplayTimeoutException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -53,7 +52,6 @@ protected AsyncReplayTimeoutException(SerializationInfo info, StreamingContext c : base(info, context) { } -#endif } /// diff --git a/src/contrib/cluster/Akka.Cluster.Sharding.Tests/Akka.Cluster.Sharding.Tests.csproj b/src/contrib/cluster/Akka.Cluster.Sharding.Tests/Akka.Cluster.Sharding.Tests.csproj index de4079d687c..e3331ead973 100644 --- a/src/contrib/cluster/Akka.Cluster.Sharding.Tests/Akka.Cluster.Sharding.Tests.csproj +++ b/src/contrib/cluster/Akka.Cluster.Sharding.Tests/Akka.Cluster.Sharding.Tests.csproj @@ -27,14 +27,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Sharding/Akka.Cluster.Sharding.csproj b/src/contrib/cluster/Akka.Cluster.Sharding/Akka.Cluster.Sharding.csproj index 9a89df9c92e..8234137e52c 100644 --- a/src/contrib/cluster/Akka.Cluster.Sharding/Akka.Cluster.Sharding.csproj +++ b/src/contrib/cluster/Akka.Cluster.Sharding/Akka.Cluster.Sharding.csproj @@ -18,10 +18,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Sharding/ShardRegion.cs b/src/contrib/cluster/Akka.Cluster.Sharding/ShardRegion.cs index efda79c5404..784271accba 100644 --- a/src/contrib/cluster/Akka.Cluster.Sharding/ShardRegion.cs +++ b/src/contrib/cluster/Akka.Cluster.Sharding/ShardRegion.cs @@ -664,11 +664,25 @@ private void Register() ? $"Coordinator [{MembersByAge.First()}] is unreachable." : $"Coordinator [{MembersByAge.First()}] is reachable."; - Log.Warning("{0}: Trying to register to coordinator at [{1}], but no acknowledgement. Total [{2}] buffered messages. [{3}]", - TypeName, - string.Join(", ", actorSelections.Select(i => i.PathString)), - TotalBufferSize, - coordinatorMessage); + var bufferSize = ShardBuffers.Count; + if (bufferSize > 0) + { + if (Log.IsWarningEnabled) + { + Log.Warning("{0}: Trying to register to coordinator at [{1}], but no acknowledgement. Total [{2}] buffered messages. [{3}]", + TypeName, + string.Join(", ", actorSelections.Select(i => i.PathString)), + TotalBufferSize, + coordinatorMessage); + } + } + else if (Log.IsDebugEnabled) + { + Log.Debug("{0}: Trying to register to coordinator at [{1}], but no acknowledgement. No buffered messages yet. [{2}]", + TypeName, + string.Join(", ", actorSelections.Select(i => i.PathString)), + coordinatorMessage); + } } else { @@ -678,8 +692,17 @@ private void Register() ? "Has Cluster Sharding been started on every node and nodes been configured with the correct role(s)?" : "Probably, no seed-nodes configured and manual cluster join not performed?"; - Log.Warning("{0}: No coordinator found to register. {1} Total [{2}] buffered messages.", - TypeName, possibleReason, TotalBufferSize); + var bufferSize = ShardBuffers.Count; + if (bufferSize > 0) + { + Log.Warning("{0}: No coordinator found to register. {1} Total [{2}] buffered messages.", + TypeName, possibleReason, TotalBufferSize); + } + else + { + Log.Debug("{0}: No coordinator found to register. {1} No buffered messages yet.", + TypeName, possibleReason); + } } } } diff --git a/src/contrib/cluster/Akka.Cluster.Tools.Tests.MultiNode/Akka.Cluster.Tools.Tests.MultiNode.csproj b/src/contrib/cluster/Akka.Cluster.Tools.Tests.MultiNode/Akka.Cluster.Tools.Tests.MultiNode.csproj index 6317a468a79..466bb53fc8a 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools.Tests.MultiNode/Akka.Cluster.Tools.Tests.MultiNode.csproj +++ b/src/contrib/cluster/Akka.Cluster.Tools.Tests.MultiNode/Akka.Cluster.Tools.Tests.MultiNode.csproj @@ -19,10 +19,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Tools.Tests/Akka.Cluster.Tools.Tests.csproj b/src/contrib/cluster/Akka.Cluster.Tools.Tests/Akka.Cluster.Tools.Tests.csproj index 664c232ced6..5ed0131226e 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools.Tests/Akka.Cluster.Tools.Tests.csproj +++ b/src/contrib/cluster/Akka.Cluster.Tools.Tests/Akka.Cluster.Tools.Tests.csproj @@ -19,14 +19,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/ClusterSingletonLeaseSpec.cs b/src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/ClusterSingletonLeaseSpec.cs index 06750f8ae88..cd9ca2522a2 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/ClusterSingletonLeaseSpec.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools.Tests/Singleton/ClusterSingletonLeaseSpec.cs @@ -117,7 +117,7 @@ public ClusterSingletonLeaseSpec() : base(ConfigurationFactory.ParseString(@" private ClusterSingletonManagerSettings NextSettings() => ClusterSingletonManagerSettings.Create(Sys).WithSingletonName(NextName()); - private string LeaseNameFor(ClusterSingletonManagerSettings settings) => $"AkkaSpec-singleton-akka://AkkaSpec/user/{settings.SingletonName}"; + private string LeaseNameFor(ClusterSingletonManagerSettings settings) => $"{Sys.Name}-singleton-akka://{Sys.Name}/user/{settings.SingletonName}"; [Fact] public void ClusterSingleton_with_lease_should_not_start_until_lease_is_available() diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Akka.Cluster.Tools.csproj b/src/contrib/cluster/Akka.Cluster.Tools/Akka.Cluster.Tools.csproj index 4b03bb18282..dc65eda72f2 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Akka.Cluster.Tools.csproj +++ b/src/contrib/cluster/Akka.Cluster.Tools/Akka.Cluster.Tools.csproj @@ -15,10 +15,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs index 4461edfa6f8..679c2d6c69b 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs @@ -525,7 +525,6 @@ public sealed class ClusterSingletonManagerIsStuckException : AkkaException /// The message that describes the error. public ClusterSingletonManagerIsStuckException(string message) : base(message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -534,7 +533,6 @@ public ClusterSingletonManagerIsStuckException(string message) : base(message) { public ClusterSingletonManagerIsStuckException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/contrib/cluster/Akka.DistributedData.Tests.MultiNode/Akka.DistributedData.Tests.MultiNode.csproj b/src/contrib/cluster/Akka.DistributedData.Tests.MultiNode/Akka.DistributedData.Tests.MultiNode.csproj index 5f7445c71a1..205ab0ae536 100644 --- a/src/contrib/cluster/Akka.DistributedData.Tests.MultiNode/Akka.DistributedData.Tests.MultiNode.csproj +++ b/src/contrib/cluster/Akka.DistributedData.Tests.MultiNode/Akka.DistributedData.Tests.MultiNode.csproj @@ -24,10 +24,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.DistributedData.Tests/Akka.DistributedData.Tests.csproj b/src/contrib/cluster/Akka.DistributedData.Tests/Akka.DistributedData.Tests.csproj index f1e12967f2b..f54d7a07fa8 100644 --- a/src/contrib/cluster/Akka.DistributedData.Tests/Akka.DistributedData.Tests.csproj +++ b/src/contrib/cluster/Akka.DistributedData.Tests/Akka.DistributedData.Tests.csproj @@ -20,10 +20,6 @@ - - $(DefineConstants);SERIALIZABLE - - $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.DistributedData/Akka.DistributedData.csproj b/src/contrib/cluster/Akka.DistributedData/Akka.DistributedData.csproj index 9169c811907..8927f87542e 100644 --- a/src/contrib/cluster/Akka.DistributedData/Akka.DistributedData.csproj +++ b/src/contrib/cluster/Akka.DistributedData/Akka.DistributedData.csproj @@ -14,9 +14,9 @@ - - $(DefineConstants);CORECLR - + + + $(DefineConstants);RELEASE diff --git a/src/contrib/cluster/Akka.DistributedData/Durable/Messages.cs b/src/contrib/cluster/Akka.DistributedData/Durable/Messages.cs index d4d85508434..52f24588f42 100644 --- a/src/contrib/cluster/Akka.DistributedData/Durable/Messages.cs +++ b/src/contrib/cluster/Akka.DistributedData/Durable/Messages.cs @@ -91,11 +91,9 @@ public LoadFailedException(string message, Exception cause) : base(message, caus { } -#if SERIALIZATION public LoadFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } public sealed class DurableDataEnvelope : IReplicatorMessage, IEquatable diff --git a/src/contrib/cluster/Akka.DistributedData/Replicator.Messages.cs b/src/contrib/cluster/Akka.DistributedData/Replicator.Messages.cs index 4109982b925..e9800b7315c 100644 --- a/src/contrib/cluster/Akka.DistributedData/Replicator.Messages.cs +++ b/src/contrib/cluster/Akka.DistributedData/Replicator.Messages.cs @@ -997,6 +997,7 @@ public override bool Equals(object obj) /// /// TBD /// + [Serializable] public class DataDeletedException : Exception { /// @@ -1006,6 +1007,13 @@ public class DataDeletedException : Exception public DataDeletedException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected DataDeletedException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public interface IReplicatorMessage { } diff --git a/src/contrib/dependencyinjection/Akka.DI.Core/Akka.DI.Core.csproj b/src/contrib/dependencyinjection/Akka.DI.Core/Akka.DI.Core.csproj index c4e9bda2f64..c4c0ea24639 100644 --- a/src/contrib/dependencyinjection/Akka.DI.Core/Akka.DI.Core.csproj +++ b/src/contrib/dependencyinjection/Akka.DI.Core/Akka.DI.Core.csproj @@ -13,15 +13,11 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE - + diff --git a/src/contrib/dependencyinjection/Akka.DI.Core/Extensions.cs b/src/contrib/dependencyinjection/Akka.DI.Core/Extensions.cs index 4d9ebc728aa..d8b3b732e30 100644 --- a/src/contrib/dependencyinjection/Akka.DI.Core/Extensions.cs +++ b/src/contrib/dependencyinjection/Akka.DI.Core/Extensions.cs @@ -11,9 +11,6 @@ using System.Reflection; using Akka.Actor; -#if CORECLR -using Microsoft.Extensions.DependencyModel; -#endif namespace Akka.DI.Core { /// @@ -78,28 +75,7 @@ public static Type GetTypeValue(this string typeName) /// The list of loaded assemblies private static IEnumerable GetLoadedAssemblies() { -#if APPDOMAIN return AppDomain.CurrentDomain.GetAssemblies(); -#elif CORECLR - var assemblies = new List(); - var dependencies = DependencyContext.Default.RuntimeLibraries; - foreach (var library in dependencies) - { - try - { - var assembly = Assembly.Load(new AssemblyName(library.Name)); - assemblies.Add(assembly); - } - catch - { - //do nothing can't if can't load assembly - } - } - return assemblies; -#else -#warning Method not implemented - throw new NotImplementedException(); -#endif } } } diff --git a/src/contrib/dependencyinjection/Akka.DI.TestKit/Akka.DI.TestKit.csproj b/src/contrib/dependencyinjection/Akka.DI.TestKit/Akka.DI.TestKit.csproj index 75e51a1cdb8..17377f73ee8 100644 --- a/src/contrib/dependencyinjection/Akka.DI.TestKit/Akka.DI.TestKit.csproj +++ b/src/contrib/dependencyinjection/Akka.DI.TestKit/Akka.DI.TestKit.csproj @@ -14,14 +14,6 @@ - - $(DefineConstants);APPDOMAIN - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/persistence/Akka.Persistence.Query.Sql/Akka.Persistence.Query.Sql.csproj b/src/contrib/persistence/Akka.Persistence.Query.Sql/Akka.Persistence.Query.Sql.csproj index 8c2fe06fc9d..a80b880462a 100644 --- a/src/contrib/persistence/Akka.Persistence.Query.Sql/Akka.Persistence.Query.Sql.csproj +++ b/src/contrib/persistence/Akka.Persistence.Query.Sql/Akka.Persistence.Query.Sql.csproj @@ -16,10 +16,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/persistence/Akka.Persistence.Sql.Common/Akka.Persistence.Sql.Common.csproj b/src/contrib/persistence/Akka.Persistence.Sql.Common/Akka.Persistence.Sql.Common.csproj index 47c45e97196..f45af5faf11 100644 --- a/src/contrib/persistence/Akka.Persistence.Sql.Common/Akka.Persistence.Sql.Common.csproj +++ b/src/contrib/persistence/Akka.Persistence.Sql.Common/Akka.Persistence.Sql.Common.csproj @@ -7,6 +7,7 @@ $(NetStandardLibVersion) $(AkkaPackageTags);persistence;eventsource;sql true + beta @@ -17,14 +18,10 @@ - + - - $(DefineConstants);CORECLR;CONFIGURATION - - $(DefineConstants);RELEASE diff --git a/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/BatchingSqlJournal.cs b/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/BatchingSqlJournal.cs index 19f24f30044..36aeb315709 100644 --- a/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/BatchingSqlJournal.cs +++ b/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/BatchingSqlJournal.cs @@ -13,6 +13,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using Akka.Actor; @@ -22,6 +23,7 @@ using Akka.Persistence.Journal; using Akka.Serialization; using Akka.Util; +using Akka.Util.Internal; namespace Akka.Persistence.Sql.Common.Journal { @@ -256,14 +258,12 @@ protected BatchingSqlJournalSetup(Config config, QueryConfiguration namingConven throw ConfigurationException.NullOrEmptyConfig(); var connectionString = config.GetString("connection-string", null); -#if CONFIGURATION if (string.IsNullOrWhiteSpace(connectionString)) { connectionString = System.Configuration.ConfigurationManager .ConnectionStrings[config.GetString("connection-string-name", "DefaultConnection")]? .ConnectionString; } -#endif if (string.IsNullOrWhiteSpace(connectionString)) throw new ConfigurationException("No connection string for Sql Event Journal was specified"); @@ -360,11 +360,13 @@ private sealed class ChunkExecutionFailure : IDeadLetterSuppression { public Exception Cause { get; } public IJournalRequest[] Requests { get; } + public int ChunkId { get; } - public ChunkExecutionFailure(Exception cause, IJournalRequest[] requests) + public ChunkExecutionFailure(Exception cause, IJournalRequest[] requests, int chunkId) { Cause = cause; Requests = requests; + ChunkId = chunkId; } } @@ -373,14 +375,12 @@ private sealed class BatchComplete public readonly int ChunkId; public readonly int OperationCount; public readonly TimeSpan TimeSpent; - public readonly Exception Cause; - public BatchComplete(int chunkId, int operationCount, TimeSpan timeSpent, Exception cause = null) + public BatchComplete(int chunkId, int operationCount, TimeSpan timeSpent) { ChunkId = chunkId; TimeSpent = timeSpent; OperationCount = operationCount; - Cause = cause; } } @@ -532,11 +532,19 @@ public RequestChunk(int chunkId, IJournalRequest[] requests) /// protected readonly ILoggingAdapter Log; + private readonly Queue<(IJournalRequest request, long id)>[] _buffers; + + /// + /// Buffer for write requests that are waiting to be served when next DB connection will be released. + /// + private Queue<(IJournalRequest request, long id)> WriteBuffer => _buffers[0]; + /// - /// Buffer for requests that are waiting to be served when next DB connection will be released. - /// This object access is NOT thread safe. + /// Buffer for read requests that are waiting to be served when next DB connection will be released. /// - protected readonly Queue Buffer; + private Queue<(IJournalRequest request, long id)> ReadBuffer => _buffers[1]; + + private readonly AtomicCounterLong _bufferIdCounter; private readonly Dictionary> _persistenceIdSubscribers; private readonly Dictionary> _tagSubscribers; @@ -560,7 +568,13 @@ protected BatchingSqlJournal(BatchingSqlJournalSetup setup) _newEventSubscriber = new HashSet(); _remainingOperations = Setup.MaxConcurrentOperations; - Buffer = new Queue(Setup.MaxBatchSize); + _buffers = new[] + { + new Queue<(IJournalRequest, long)>(Setup.MaxBatchSize), + new Queue<(IJournalRequest, long)>(Setup.MaxBatchSize) + }; + _bufferIdCounter = new AtomicCounterLong(0); + _serialization = Context.System.Serialization; Log = Context.GetLogger(); _circuitBreaker = CircuitBreaker.Create( @@ -733,31 +747,65 @@ protected sealed override bool Receive(object message) private void FailChunkExecution(ChunkExecutionFailure message) { + _remainingOperations++; var cause = message.Cause; - Log.Error(cause, "Failed to execute chunk for {0} requests", message.Requests.Length); - - foreach (var req in message.Requests) + Log.Error(cause, "An error occurred during event batch processing. ChunkId: [{0}], batched requests: [{1}]", message.ChunkId, message.Requests.Length); + + foreach (var request in message.Requests) { - switch (req) + switch (request) { - case WriteMessages write: - var atomicWriteCount = write.Messages.OfType().Count(); - write.PersistentActor.Tell(new WriteMessagesFailed(cause, atomicWriteCount)); + case WriteMessages req: + { + var atomicWriteCount = req.Messages.OfType().Count(); + var actorInstanceId = req.ActorInstanceId; + var aRef = req.PersistentActor; + + aRef.Tell(new WriteMessagesFailed(cause, atomicWriteCount)); + foreach (var envelope in req.Messages) + { + if (!(envelope is AtomicWrite write)) + continue; + + var writes = (IImmutableList)write.Payload; + foreach (var unadapted in writes) + { + if (cause is DbException) + { + // database-related exceptions should result in failure + aRef.Tell(new WriteMessageFailure(unadapted, cause, actorInstanceId), unadapted.Sender); + } + else + { + aRef.Tell(new WriteMessageRejected(unadapted, cause, actorInstanceId), unadapted.Sender); + } + } + } break; + } + + case DeleteMessagesTo delete: + delete.PersistentActor.Tell(new DeleteMessagesFailure(cause, delete.ToSequenceNr), ActorRefs.NoSender); + break; + case ReplayMessages replay: replay.PersistentActor.Tell(new ReplayMessagesFailure(cause)); break; - case DeleteMessagesTo delete: - delete.PersistentActor.Tell(new DeleteMessagesFailure(cause, delete.ToSequenceNr)); - break; + case ReplayTaggedMessages replayTagged: replayTagged.ReplyTo.Tell(new ReplayMessagesFailure(cause)); break; + case ReplayAllEvents replayAll: replayAll.ReplyTo.Tell(new EventReplayFailure(cause)); break; + + default: + throw new Exception($"Unknown persistence journal request type [{request.GetType()}]"); } } + + TryProcess(); } #region subscriptions @@ -829,10 +877,20 @@ private void NotifyPersistenceIdChanged(string persistenceId) /// TBD protected void BatchRequest(IJournalRequest message) { - if (Buffer.Count > Setup.MaxBufferSize) + if (WriteBuffer.Count + ReadBuffer.Count > Setup.MaxBufferSize) + { OnBufferOverflow(message); + } else - Buffer.Enqueue(message); + { + var id = _bufferIdCounter.GetAndIncrement(); + // Enqueue writes and delete operation requests into the write queue, + // else if they are query operations, enqueue them into the read queue + if (message is WriteMessages || message is DeleteMessagesTo) + WriteBuffer.Enqueue((message, id)); + else + ReadBuffer.Enqueue((message, id)); + } TryProcess(); } @@ -870,25 +928,28 @@ protected virtual void OnBufferOverflow(IJournalMessage request) private void TryProcess() { - if (_remainingOperations > 0 && Buffer.Count > 0) + if (_remainingOperations > 0 && (WriteBuffer.Count > 0 || ReadBuffer.Count > 0)) { _remainingOperations--; var chunk = DequeueChunk(_remainingOperations); var context = Context; _circuitBreaker.WithCircuitBreaker(() => ExecuteChunk(chunk, context)) - .PipeTo(Self, failure: ex => new ChunkExecutionFailure(ex, chunk.Requests)); + .PipeTo(Self, failure: ex => new ChunkExecutionFailure(ex, chunk.Requests, chunk.ChunkId)); } } private async Task ExecuteChunk(RequestChunk chunk, IActorContext context) { - Exception cause = null; + var writeResults = new Queue(); + var isWriteOperation = false; var stopwatch = new Stopwatch(); using (var connection = CreateConnection(Setup.ConnectionString)) { await connection.OpenAsync(); + // In the grand scheme of thing, using a transaction in an all read batch operation + // should not hurt performance by much, because it is done only once at the start. using (var tx = connection.BeginTransaction(Setup.IsolationLevel)) using (var command = (TCommand)connection.CreateCommand()) { @@ -897,21 +958,23 @@ private async Task ExecuteChunk(RequestChunk chunk, IActorContext try { stopwatch.Start(); - for (int i = 0; i < chunk.Requests.Length; i++) + // This looks dangerous at a glance, but we have separated read and write operations + // in the DequeueChunk method. + foreach (var req in chunk.Requests) { - var req = chunk.Requests[i]; - switch (req) { case WriteMessages msg: - await HandleWriteMessages(msg, command); - break; - case ReplayMessages msg: - await HandleReplayMessages(msg, command, context); + isWriteOperation = true; + writeResults.Enqueue(await HandleWriteMessages(msg, command)); break; case DeleteMessagesTo msg: + isWriteOperation = true; await HandleDeleteMessagesTo(msg, command); break; + case ReplayMessages msg: + await HandleReplayMessages(msg, command, context); + break; case ReplayTaggedMessages msg: await HandleReplayTaggedMessages(msg, command); break; @@ -926,21 +989,19 @@ private async Task ExecuteChunk(RequestChunk chunk, IActorContext break; } } - tx.Commit(); - - if (CanPublish) - { - for (int i = 0; i < chunk.Requests.Length; i++) - { - context.System.EventStream.Publish(chunk.Requests[i]); - } - } } - catch (Exception e) + catch (Exception e1) { - cause = e; - tx.Rollback(); + try + { + tx.Rollback(); + } + catch (Exception e2) + { + throw new AggregateException(e2, e1); + } + throw; } finally { @@ -949,7 +1010,33 @@ private async Task ExecuteChunk(RequestChunk chunk, IActorContext } } - return new BatchComplete(chunk.ChunkId, chunk.Requests.Length, stopwatch.Elapsed, cause); + if (CanPublish) + { + foreach (var request in chunk.Requests) + { + context.System.EventStream.Publish(request); + } + } + + if (isWriteOperation) + { + foreach (var request in chunk.Requests) + { + switch (request) + { + case WriteMessages _: + writeResults.Dequeue().FinalizeSuccess(this); + break; + case DeleteMessagesTo req: + req.PersistentActor.Tell(new DeleteMessagesSuccess(req.ToSequenceNr)); + break; + default: + throw new Exception($"Unknown database write operation {request.GetType()}"); + } + } + } + + return new BatchComplete(chunk.ChunkId, chunk.Requests.Length, stopwatch.Elapsed); } protected virtual async Task HandleDeleteMessagesTo(DeleteMessagesTo req, TCommand command) @@ -957,35 +1044,24 @@ protected virtual async Task HandleDeleteMessagesTo(DeleteMessagesTo req, TComma var toSequenceNr = req.ToSequenceNr; var persistenceId = req.PersistenceId; - try - { - var highestSequenceNr = await ReadHighestSequenceNr(persistenceId, command); + var highestSequenceNr = await ReadHighestSequenceNr(persistenceId, command); - command.CommandText = DeleteBatchSql; - command.Parameters.Clear(); - AddParameter(command, "@PersistenceId", DbType.String, persistenceId); - AddParameter(command, "@ToSequenceNr", DbType.Int64, toSequenceNr); - - await command.ExecuteNonQueryAsync(); + command.CommandText = DeleteBatchSql; + command.Parameters.Clear(); + AddParameter(command, "@PersistenceId", DbType.String, persistenceId); + AddParameter(command, "@ToSequenceNr", DbType.Int64, toSequenceNr); - if (highestSequenceNr <= toSequenceNr) - { - command.CommandText = UpdateSequenceNrSql; - command.Parameters.Clear(); + await command.ExecuteNonQueryAsync(); - AddParameter(command, "@PersistenceId", DbType.String, persistenceId); - AddParameter(command, "@SequenceNr", DbType.Int64, highestSequenceNr); + if (highestSequenceNr <= toSequenceNr) + { + command.CommandText = UpdateSequenceNrSql; + command.Parameters.Clear(); - await command.ExecuteNonQueryAsync(); - } + AddParameter(command, "@PersistenceId", DbType.String, persistenceId); + AddParameter(command, "@SequenceNr", DbType.Int64, highestSequenceNr); - var response = new DeleteMessagesSuccess(toSequenceNr); - req.PersistentActor.Tell(response); - } - catch (Exception cause) - { - var response = new DeleteMessagesFailure(cause, toSequenceNr); - req.PersistentActor.Tell(response, ActorRefs.NoSender); + await command.ExecuteNonQueryAsync(); } } @@ -1168,116 +1244,47 @@ protected virtual async Task HandleReplayMessages(ReplayMessages req, TCommand c } } - private async Task HandleWriteMessages(WriteMessages req, TCommand command) + private async Task HandleWriteMessages(WriteMessages req, TCommand command) { - IJournalResponse summary = null; - var responses = new List<(IJournalResponse, IActorRef)>(); var tags = new HashSet(); var persistenceIds = new HashSet(); - var actorInstanceId = req.ActorInstanceId; - var atomicWriteCount = req.Messages.OfType().Count(); - try - { - command.CommandText = InsertEventSql; + command.CommandText = InsertEventSql; - var tagBuilder = new StringBuilder(16); // magic number + var tagBuilder = new StringBuilder(16); // magic number - foreach (var envelope in req.Messages) + foreach (var envelope in req.Messages.OfType()) + { + var writes = (IImmutableList)envelope.Payload; + foreach (var unadapted in writes) { - if (envelope is AtomicWrite write) + command.Parameters.Clear(); + tagBuilder.Clear(); + + var persistent = AdaptToJournal(unadapted); + if (persistent.Payload is Tagged tagged) { - var writes = (IImmutableList)write.Payload; - foreach (var unadapted in writes) + if (tagged.Tags.Count != 0) { - try + tagBuilder.Append(';'); + foreach (var tag in tagged.Tags) { - command.Parameters.Clear(); - tagBuilder.Clear(); - - var persistent = AdaptToJournal(unadapted); - if (persistent.Payload is Tagged tagged) - { - if (tagged.Tags.Count != 0) - { - tagBuilder.Append(';'); - foreach (var tag in tagged.Tags) - { - tags.Add(tag); - tagBuilder.Append(tag).Append(';'); - } - } - persistent = persistent.WithPayload(tagged.Payload); - } - - WriteEvent(command, persistent.WithTimestamp(DateTime.UtcNow.Ticks), tagBuilder.ToString()); - - await command.ExecuteNonQueryAsync(); - - var response = (new WriteMessageSuccess(unadapted, actorInstanceId), unadapted.Sender); - responses.Add(response); - persistenceIds.Add(persistent.PersistenceId); - } - catch (DbException cause) - { - // database-related exceptions should result in failure - summary = new WriteMessagesFailed(cause, atomicWriteCount); - var response = (new WriteMessageFailure(unadapted, cause, actorInstanceId), unadapted.Sender); - responses.Add(response); - } - catch (Exception cause) - { - //TODO: this scope wraps atomic write. Atomic writes have all-or-nothing commits. - // so we should revert transaction here. But we need to check how this affect performance. - - var response = (new WriteMessageRejected(unadapted, cause, actorInstanceId), unadapted.Sender); - responses.Add(response); + tags.Add(tag); + tagBuilder.Append(tag).Append(';'); } } + persistent = persistent.WithPayload(tagged.Payload); } - else - { - //TODO: other cases? - var response = (new LoopMessageSuccess(envelope.Payload, actorInstanceId), envelope.Sender); - responses.Add(response); - } - } - if (HasTagSubscribers && tags.Count != 0) - { - foreach (var tag in tags) - { - NotifyTagChanged(tag); - } - } + WriteEvent(command, persistent.WithTimestamp(DateTime.UtcNow.Ticks), tagBuilder.ToString()); - if (HasPersistenceIdSubscribers) - { - foreach (var persistenceId in persistenceIds) - { - NotifyPersistenceIdChanged(persistenceId); - } - } + await command.ExecuteNonQueryAsync(); - if (HasNewEventsSubscribers) - { - NotifyNewEventAppended(); + persistenceIds.Add(persistent.PersistenceId); } - - summary = summary ?? WriteMessagesSuccessful.Instance; - } - catch (Exception cause) - { - summary = new WriteMessagesFailed(cause, atomicWriteCount); } - var aref = req.PersistentActor; - - aref.Tell(summary); - foreach (var r in responses) - { - aref.Tell(r.Item1, r.Item2); - } + return new WriteMessagesResult(req, tags, persistenceIds); } /// @@ -1388,29 +1395,111 @@ protected void AddParameter(TCommand command, string paramName, DbType dbType, o /// Parameter to customize protected virtual void PreAddParameterToCommand(TCommand command, DbParameter param) { } + /// + /// Select the buffer that has the smallest id on its first item, retrieve a maximum Setup.MaxBatchSize + /// items from it, and return it as a chunk that needs to be batched + /// private RequestChunk DequeueChunk(int chunkId) { - var operationsCount = Math.Min(Buffer.Count, Setup.MaxBatchSize); - var array = new IJournalRequest[operationsCount]; - for (int i = 0; i < operationsCount; i++) + var currentBuffer = _buffers + .Where(q => q.Count > 0) + .OrderBy(q => q.Peek().id).First(); + + var operations = new List(); + if (ReferenceEquals(currentBuffer, WriteBuffer)) { - var req = Buffer.Dequeue(); - array[i] = req; + // Stop dequeuing when we encounter another type of write operation request + // We don't batch delete and writes in the same batch, reason being a database + // can be deadlocked if write and delete happens in the same transaction + var writeType = currentBuffer.Peek().request.GetType(); + while(currentBuffer.Count > 0 && currentBuffer.Peek().request.GetType() == writeType) + { + operations.Add(currentBuffer.Dequeue().request); + if (operations.Count == Setup.MaxBatchSize) + break; + } } - - return new RequestChunk(chunkId, array); + else + { + while(currentBuffer.Count > 0) + { + operations.Add(currentBuffer.Dequeue().request); + if (operations.Count == Setup.MaxBatchSize) + break; + } + } + + return new RequestChunk(chunkId, operations.ToArray()); } private void CompleteBatch(BatchComplete msg) { _remainingOperations++; - if (msg.Cause != null) + Log.Debug("Completed batch (chunkId: {0}) of {1} operations in {2} milliseconds", msg.ChunkId, msg.OperationCount, msg.TimeSpent.TotalMilliseconds); + + TryProcess(); + } + + private class WriteMessagesResult + { + private readonly WriteMessages _request; + + private readonly ImmutableHashSet _tags; + private readonly ImmutableHashSet _persistenceIds; + + public WriteMessagesResult( + WriteMessages request, + IEnumerable tags, + IEnumerable persistenceIds) { - Log.Error(msg.Cause, "An error occurred during event batch processing (chunkId: {0})", msg.ChunkId); + _request = request; + _tags = tags.ToImmutableHashSet(); + _persistenceIds = persistenceIds.ToImmutableHashSet(); } - else Log.Debug("Completed batch (chunkId: {0}) of {1} operations in {2} milliseconds", msg.ChunkId, msg.OperationCount, msg.TimeSpent.TotalMilliseconds); - TryProcess(); + public void FinalizeSuccess(BatchingSqlJournal journal) + { + var actorInstanceId = _request.ActorInstanceId; + var aRef = _request.PersistentActor; + aRef.Tell(WriteMessagesSuccessful.Instance); + + foreach (var envelope in _request.Messages) + { + if (!(envelope is AtomicWrite write)) + { + aRef.Tell(new LoopMessageSuccess(envelope.Payload, actorInstanceId), envelope.Sender); + continue; + } + + var writes = (IImmutableList)write.Payload; + foreach (var unadapted in writes) + { + aRef.Tell(new WriteMessageSuccess(unadapted, actorInstanceId), unadapted.Sender); + } + } + + if (journal.HasTagSubscribers && _tags.Count != 0) + { + foreach (var tag in _tags) + { + journal.NotifyTagChanged(tag); + } + } + + if (journal.HasPersistenceIdSubscribers) + { + foreach (var persistenceId in _persistenceIds) + { + journal.NotifyPersistenceIdChanged(persistenceId); + } + } + + if (journal.HasNewEventsSubscribers) + { + journal.NotifyNewEventAppended(); + } + + } } } @@ -1434,5 +1523,15 @@ public JournalBufferOverflowException() : base( + " change it.") { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected JournalBufferOverflowException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/SqlJournal.cs b/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/SqlJournal.cs index b043c1c1cbf..8d74a49c1b0 100644 --- a/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/SqlJournal.cs +++ b/src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/SqlJournal.cs @@ -506,12 +506,10 @@ protected virtual string GetConnectionString() { var connectionString = _settings.ConnectionString; -#if CONFIGURATION if (string.IsNullOrEmpty(connectionString)) { connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[_settings.ConnectionStringName].ConnectionString; } -#endif return connectionString; } diff --git a/src/contrib/persistence/Akka.Persistence.Sql.Common/Snapshot/SqlSnapshotStore.cs b/src/contrib/persistence/Akka.Persistence.Sql.Common/Snapshot/SqlSnapshotStore.cs index ad92d5c665b..f63d246e7b3 100644 --- a/src/contrib/persistence/Akka.Persistence.Sql.Common/Snapshot/SqlSnapshotStore.cs +++ b/src/contrib/persistence/Akka.Persistence.Sql.Common/Snapshot/SqlSnapshotStore.cs @@ -149,12 +149,10 @@ protected virtual string GetConnectionString() { var connectionString = _settings.ConnectionString; -#if CONFIGURATION if (string.IsNullOrEmpty(connectionString)) { connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[_settings.ConnectionStringName].ConnectionString; } -#endif return connectionString; } diff --git a/src/contrib/persistence/Akka.Persistence.Sql.TestKit/Akka.Persistence.Sql.TestKit.csproj b/src/contrib/persistence/Akka.Persistence.Sql.TestKit/Akka.Persistence.Sql.TestKit.csproj index 65bd043c117..b4c479e7925 100644 --- a/src/contrib/persistence/Akka.Persistence.Sql.TestKit/Akka.Persistence.Sql.TestKit.csproj +++ b/src/contrib/persistence/Akka.Persistence.Sql.TestKit/Akka.Persistence.Sql.TestKit.csproj @@ -16,10 +16,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/contrib/persistence/Akka.Persistence.Sqlite.Tests/Akka.Persistence.Sqlite.Tests.csproj b/src/contrib/persistence/Akka.Persistence.Sqlite.Tests/Akka.Persistence.Sqlite.Tests.csproj index 69486b773af..a396432f1ef 100644 --- a/src/contrib/persistence/Akka.Persistence.Sqlite.Tests/Akka.Persistence.Sqlite.Tests.csproj +++ b/src/contrib/persistence/Akka.Persistence.Sqlite.Tests/Akka.Persistence.Sqlite.Tests.csproj @@ -27,10 +27,6 @@ - - $(DefineConstants);SERIALIZABLE - - $(DefineConstants);RELEASE diff --git a/src/contrib/persistence/Akka.Persistence.Sqlite/Akka.Persistence.Sqlite.csproj b/src/contrib/persistence/Akka.Persistence.Sqlite/Akka.Persistence.Sqlite.csproj index 0d52c1c8ac0..40be4c4326d 100644 --- a/src/contrib/persistence/Akka.Persistence.Sqlite/Akka.Persistence.Sqlite.csproj +++ b/src/contrib/persistence/Akka.Persistence.Sqlite/Akka.Persistence.Sqlite.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion/Akka.Serialization.Hyperion.csproj b/src/contrib/serializers/Akka.Serialization.Hyperion/Akka.Serialization.Hyperion.csproj index 13a0d932ec0..8fb0de29efb 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion/Akka.Serialization.Hyperion.csproj +++ b/src/contrib/serializers/Akka.Serialization.Hyperion/Akka.Serialization.Hyperion.csproj @@ -12,6 +12,14 @@ + + + + + + + + $(DefineConstants);RELEASE diff --git a/src/contrib/serializers/Akka.Serialization.TestKit/Akka.Serialization.TestKit.csproj b/src/contrib/serializers/Akka.Serialization.TestKit/Akka.Serialization.TestKit.csproj index a3ebeab5a37..baaf650bb5b 100644 --- a/src/contrib/serializers/Akka.Serialization.TestKit/Akka.Serialization.TestKit.csproj +++ b/src/contrib/serializers/Akka.Serialization.TestKit/Akka.Serialization.TestKit.csproj @@ -13,4 +13,9 @@ + + + $(DefineConstants);RELEASE + + \ No newline at end of file diff --git a/src/contrib/testkits/Akka.TestKit.Xunit/Internals/AkkaEqualException.cs b/src/contrib/testkits/Akka.TestKit.Xunit/Internals/AkkaEqualException.cs index af5d866b4be..93d153a5554 100644 --- a/src/contrib/testkits/Akka.TestKit.Xunit/Internals/AkkaEqualException.cs +++ b/src/contrib/testkits/Akka.TestKit.Xunit/Internals/AkkaEqualException.cs @@ -33,7 +33,6 @@ public AkkaEqualException(object expected, object actual, string format = "", pa _args = args; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -43,7 +42,7 @@ protected AkkaEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif + /// /// The message that describes the error. /// diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/AkkaEqualException.cs b/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/AkkaEqualException.cs index f66ff87be65..c0c21e6224d 100644 --- a/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/AkkaEqualException.cs +++ b/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/AkkaEqualException.cs @@ -33,7 +33,6 @@ public AkkaEqualException(object expected, object actual, string format = "", pa _args = args; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -43,7 +42,7 @@ protected AkkaEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif + /// /// The message that describes the error. /// diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/Loggers.cs b/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/Loggers.cs index 55adb6b09b3..4ea59da9e0d 100644 --- a/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/Loggers.cs +++ b/src/contrib/testkits/Akka.TestKit.Xunit2/Internals/Loggers.cs @@ -5,6 +5,7 @@ // //----------------------------------------------------------------------- +using System; using Akka.Actor; using Akka.Event; using Xunit.Abstractions; @@ -16,20 +17,44 @@ namespace Akka.TestKit.Xunit2.Internals /// public class TestOutputLogger : ReceiveActor { + private readonly ITestOutputHelper _output; + /// /// Initializes a new instance of the class. /// /// The provider used to write test output. public TestOutputLogger(ITestOutputHelper output) { - Receive(e => output.WriteLine(e.ToString())); - Receive(e => output.WriteLine(e.ToString())); - Receive(e => output.WriteLine(e.ToString())); - Receive(e => output.WriteLine(e.ToString())); + _output = output; + + Receive(HandleLogEvent); + Receive(HandleLogEvent); + Receive(HandleLogEvent); + Receive(HandleLogEvent); Receive(e => { e.LoggingBus.Subscribe(Self, typeof (LogEvent)); }); } + + private void HandleLogEvent(LogEvent e) + { + try + { + _output.WriteLine(e.ToString()); + } + catch (FormatException ex) + { + if (e.Message is LogMessage msg) + { + var message = + $"Received a malformed formatted message. Log level: [{e.LogLevel()}], Template: [{msg.Format}], args: [{string.Join(",", msg.Args)}]"; + if(e.Cause != null) + throw new AggregateException(message, ex, e.Cause); + throw new FormatException(message, ex); + } + throw; + } + } } } diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCluster.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCluster.approved.txt index c8c6f3b7c0e..8f9b3b5ccab 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCluster.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCluster.approved.txt @@ -176,6 +176,7 @@ namespace Akka.Cluster public class ClusterJoinFailedException : Akka.Actor.AkkaException { public ClusterJoinFailedException(string message) { } + protected ClusterJoinFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class ClusterScope : Akka.Actor.Scope { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveClusterTools.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveClusterTools.approved.txt index df676d7749b..3b2fd70c621 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveClusterTools.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveClusterTools.approved.txt @@ -345,6 +345,7 @@ namespace Akka.Cluster.Tools.Singleton public sealed class ClusterSingletonManagerIsStuckException : Akka.Actor.AkkaException { public ClusterSingletonManagerIsStuckException(string message) { } + public ClusterSingletonManagerIsStuckException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class ClusterSingletonManagerSettings : Akka.Actor.INoSerializationVerificationNeeded { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCoordination.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCoordination.approved.txt index 6bda7ca1ed2..1414adb9dde 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCoordination.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCoordination.approved.txt @@ -15,6 +15,7 @@ namespace Akka.Coordination { public LeaseException(string message) { } public LeaseException(string message, System.Exception innerEx) { } + protected LeaseException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class LeaseProvider : Akka.Actor.IExtension { @@ -43,6 +44,7 @@ namespace Akka.Coordination { public LeaseTimeoutException(string message) { } public LeaseTimeoutException(string message, System.Exception innerEx) { } + protected LeaseTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class LeaseUsageSettings { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 2def445471a..8aee29b1c71 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -151,21 +151,26 @@ namespace Akka.Actor public ActorInitializationException(string message) { } public ActorInitializationException(string message, System.Exception cause) { } public ActorInitializationException(Akka.Actor.IActorRef actor, string message, System.Exception cause = null) { } + protected ActorInitializationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public Akka.Actor.IActorRef Actor { get; set; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public override string ToString() { } } public class ActorInterruptedException : Akka.Actor.AkkaException { public ActorInterruptedException(string message = null, System.Exception cause = null) { } + protected ActorInterruptedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class ActorKilledException : Akka.Actor.AkkaException { public ActorKilledException() { } public ActorKilledException(string message) { } + protected ActorKilledException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class ActorNotFoundException : Akka.Actor.AkkaException { public ActorNotFoundException() { } + protected ActorNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public ActorNotFoundException(string message, System.Exception innerException = null) { } } public abstract class ActorPath : Akka.Util.ISurrogated, System.IComparable, System.IEquatable @@ -384,7 +389,7 @@ namespace Akka.Actor where T : class, Akka.Actor.IExtension where TI : Akka.Actor.IExtensionId { } } - public sealed class Address : Akka.Util.ISurrogated, System.IComparable, System.IComparable, System.IEquatable + public sealed class Address : Akka.Util.ISurrogated, System.ICloneable, System.IComparable, System.IComparable, System.IEquatable { public static readonly Akka.Actor.Address AllSystems; public static readonly System.Collections.Generic.IComparer Comparer; @@ -424,6 +429,7 @@ namespace Akka.Actor { protected AkkaException() { } protected AkkaException(string message, System.Exception cause = null) { } + protected AkkaException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } protected System.Exception Cause { get; } } public class AllForOneStrategy : Akka.Actor.SupervisorStrategy, System.IEquatable @@ -458,6 +464,7 @@ namespace Akka.Actor public class AskTimeoutException : Akka.Actor.AkkaException { public AskTimeoutException(string message) { } + protected AskTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class BootstrapSetup : Akka.Actor.Setup.Setup { @@ -577,7 +584,9 @@ namespace Akka.Actor public class DeathPactException : Akka.Actor.AkkaException { public DeathPactException(Akka.Actor.IActorRef deadActor) { } + protected DeathPactException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public Akka.Actor.IActorRef DeadActor { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static Decider { @@ -1160,10 +1169,12 @@ namespace Akka.Actor public class IllegalActorNameException : Akka.Actor.AkkaException { public IllegalActorNameException(string message) { } + protected IllegalActorNameException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class IllegalActorStateException : Akka.Actor.AkkaException { public IllegalActorStateException(string message) { } + protected IllegalActorStateException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class Inbox : Akka.Actor.ICanWatch, Akka.Actor.IInboxable, System.IDisposable { @@ -1204,11 +1215,13 @@ namespace Akka.Actor { public InvalidActorNameException(string message) { } public InvalidActorNameException(string message, System.Exception innerException) { } + protected InvalidActorNameException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class InvalidMessageException : Akka.Actor.AkkaException { public InvalidMessageException() { } public InvalidMessageException(string message) { } + protected InvalidMessageException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class Kill : Akka.Actor.IAutoReceivedMessage { @@ -1293,6 +1306,7 @@ namespace Akka.Actor public LoggerInitializationException() { } public LoggerInitializationException(string message) { } public LoggerInitializationException(string message, System.Exception cause = null) { } + protected LoggerInitializationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } [Akka.Annotations.InternalApiAttribute()] public abstract class MinimalActorRef : Akka.Actor.InternalActorRefBase, Akka.Actor.IActorRefScope @@ -1374,11 +1388,17 @@ namespace Akka.Actor public class PostRestartException : Akka.Actor.ActorInitializationException { public PostRestartException(Akka.Actor.IActorRef actor, System.Exception cause, System.Exception originalCause) { } + protected PostRestartException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public System.Exception OriginalCause { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public class PreRestartException : Akka.Actor.AkkaException + public class PreRestartException : Akka.Actor.ActorInitializationException { public PreRestartException(Akka.Actor.IActorRef actor, System.Exception restartException, System.Exception cause, object optionalMessage) { } + protected PreRestartException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public object OptionalMessage { get; } + public System.Exception RestartException { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class Props : Akka.Util.ISurrogated, System.IEquatable { @@ -1577,6 +1597,7 @@ namespace Akka.Actor public sealed class SchedulerException : Akka.Actor.AkkaException { public SchedulerException(string message) { } + protected SchedulerException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static SchedulerExtensions { @@ -1692,6 +1713,7 @@ namespace Akka.Actor public class StashOverflowException : Akka.Actor.AkkaException { public StashOverflowException(string message, System.Exception cause = null) { } + protected StashOverflowException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public abstract class Status { @@ -2206,6 +2228,7 @@ namespace Akka.Configuration { public ConfigurationException(string message) { } public ConfigurationException(string message, System.Exception exception) { } + protected ConfigurationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public static Akka.Configuration.ConfigurationException NullOrEmptyConfig(string path = null) { } } public class ConfigurationFactory @@ -2642,6 +2665,7 @@ namespace Akka.Dispatch public class RejectedExecutionException : Akka.Actor.AkkaException { public RejectedExecutionException(string message = null, System.Exception inner = null) { } + protected RejectedExecutionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class ThreadPoolConfig { @@ -3192,6 +3216,7 @@ namespace Akka.IO.Buffers public class BufferPoolAllocationException : Akka.Actor.AkkaException { public BufferPoolAllocationException(string message) { } + protected BufferPoolAllocationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public interface IBufferPool { @@ -3973,6 +3998,7 @@ namespace Akka.Pattern { public IllegalStateException(string message) { } public IllegalStateException(string message, System.Exception innerEx) { } + protected IllegalStateException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class OpenCircuitException : Akka.Actor.AkkaException { @@ -3983,7 +4009,9 @@ namespace Akka.Pattern public OpenCircuitException(string message, System.Exception cause, System.TimeSpan remainingDuration) { } public OpenCircuitException(System.Exception cause) { } public OpenCircuitException(System.Exception cause, System.TimeSpan remainingDuration) { } + protected OpenCircuitException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public System.TimeSpan RemainingDuration { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static RetrySupport { @@ -3995,6 +4023,7 @@ namespace Akka.Pattern public class UserCalledFailException : Akka.Actor.AkkaException { public UserCalledFailException() { } + protected UserCalledFailException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } namespace Akka.Routing diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveDistributedData.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveDistributedData.approved.txt index fa45f6893cd..56ce958c6a4 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveDistributedData.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveDistributedData.approved.txt @@ -42,6 +42,7 @@ namespace Akka.DistributedData public class DataDeletedException : System.Exception { public DataDeletedException(string message) { } + protected DataDeletedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class Delete : Akka.Actor.INoSerializationVerificationNeeded, System.IEquatable { @@ -981,6 +982,7 @@ namespace Akka.DistributedData.Durable { public LoadFailedException(string message) { } public LoadFailedException(string message, System.Exception cause) { } + public LoadFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class Store { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistence.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistence.approved.txt index 11c5c2a144d..44dce9a7cb9 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistence.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistence.approved.txt @@ -362,6 +362,7 @@ namespace Akka.Persistence public MaxUnconfirmedMessagesExceededException() { } public MaxUnconfirmedMessagesExceededException(string message) { } public MaxUnconfirmedMessagesExceededException(string message, System.Exception innerException) { } + protected MaxUnconfirmedMessagesExceededException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class Persistence : Akka.Actor.ExtensionIdProvider { @@ -505,6 +506,7 @@ namespace Akka.Persistence { public RecoveryTimedOutException() { } public RecoveryTimedOutException(string message, System.Exception cause = null) { } + public RecoveryTimedOutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class ReplayMessages : Akka.Actor.INoSerializationVerificationNeeded, Akka.Persistence.IJournalMessage, Akka.Persistence.IJournalRequest, Akka.Persistence.IPersistenceMessage, System.IEquatable { @@ -827,6 +829,7 @@ namespace Akka.Persistence.Journal { public AsyncReplayTimeoutException() { } public AsyncReplayTimeoutException(string message) { } + protected AsyncReplayTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public abstract class AsyncWriteJournal : Akka.Persistence.Journal.WriteJournalBase, Akka.Persistence.Journal.IAsyncRecovery { @@ -1109,6 +1112,7 @@ namespace Akka.Persistence.Snapshot public NoSnapshotStoreException() { } public NoSnapshotStoreException(string message) { } public NoSnapshotStoreException(string message, System.Exception innerException) { } + protected NoSnapshotStoreException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } public class SnapshotEntry diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistenceSqlCommon.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistenceSqlCommon.approved.txt index 33f960fce76..5e78ff5e2ea 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistenceSqlCommon.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApprovePersistenceSqlCommon.approved.txt @@ -74,7 +74,6 @@ namespace Akka.Persistence.Sql.Common.Journal where TConnection : System.Data.Common.DbConnection where TCommand : System.Data.Common.DbCommand { - protected readonly System.Collections.Generic.Queue Buffer; protected readonly bool CanPublish; protected const int IsDeletedIndex = 3; protected readonly Akka.Event.ILoggingAdapter Log; @@ -188,6 +187,7 @@ namespace Akka.Persistence.Sql.Common.Journal { public static readonly Akka.Persistence.Sql.Common.Journal.JournalBufferOverflowException Instance; public JournalBufferOverflowException() { } + protected JournalBufferOverflowException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class JournalEntry { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt index f3fc63faff7..766d4f50243 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt @@ -284,6 +284,7 @@ namespace Akka.Remote public class RemoteTransportException : Akka.Actor.AkkaException { public RemoteTransportException(string message, System.Exception cause = null) { } + protected RemoteTransportException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class RemoteWatcher : Akka.Actor.UntypedActor, Akka.Dispatch.IRequiresMessageQueue { @@ -434,11 +435,11 @@ namespace Akka.Remote.Serialization public override string Manifest(object obj) { } public override byte[] ToBinary(object obj) { } } - public sealed class PrimitiveSerializers : Akka.Serialization.Serializer + public sealed class PrimitiveSerializers : Akka.Serialization.SerializerWithStringManifest { public PrimitiveSerializers(Akka.Actor.ExtendedActorSystem system) { } - public override bool IncludeManifest { get; } - public override object FromBinary(byte[] bytes, System.Type type) { } + public override object FromBinary(byte[] bytes, string manifest) { } + public override string Manifest(object obj) { } public override byte[] ToBinary(object obj) { } } public class ProtobufSerializer : Akka.Serialization.Serializer @@ -502,6 +503,7 @@ namespace Akka.Remote.Transport public class AkkaProtocolException : Akka.Actor.AkkaException { public AkkaProtocolException(string message, System.Exception cause = null) { } + protected AkkaProtocolException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class AssociateAttempt : Akka.Remote.Transport.Activity { @@ -602,6 +604,7 @@ namespace Akka.Remote.Transport public class InvalidAssociationException : Akka.Actor.AkkaException { public InvalidAssociationException(string message, System.Exception cause = null) { } + protected InvalidAssociationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class ListenAttempt : Akka.Remote.Transport.Activity { diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt index 1e1b438b049..af1c1fda081 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt @@ -12,11 +12,13 @@ namespace Akka.Streams public sealed class AbruptStageTerminationException : System.Exception { public AbruptStageTerminationException(Akka.Streams.Stage.GraphStageLogic logic) { } + protected AbruptStageTerminationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class AbruptTerminationException : System.Exception { public readonly Akka.Actor.IActorRef Actor; public AbruptTerminationException(Akka.Actor.IActorRef actor) { } + protected AbruptTerminationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static ActorAttributes { @@ -280,11 +282,13 @@ namespace Akka.Streams public class BindFailedException : Akka.Streams.StreamTcpException { public static readonly Akka.Streams.BindFailedException Instance; + protected BindFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class BufferOverflowException : System.Exception { public BufferOverflowException(string message) { } public BufferOverflowException(string message, System.Exception innerException) { } + protected BufferOverflowException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class ClosedShape : Akka.Streams.Shape { @@ -298,6 +302,7 @@ namespace Akka.Streams { public ConnectionException(string message) { } public ConnectionException(string message, System.Exception innerException) { } + protected ConnectionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static Construct { @@ -757,12 +762,14 @@ namespace Akka.Streams public sealed class InvalidPartnerActorException : Akka.Pattern.IllegalStateException { public InvalidPartnerActorException(Akka.Actor.IActorRef expectedRef, Akka.Actor.IActorRef gotRef, string message) { } + protected InvalidPartnerActorException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public Akka.Actor.IActorRef ExpectedRef { get; } public Akka.Actor.IActorRef GotRef { get; } } public sealed class InvalidSequenceNumberException : Akka.Pattern.IllegalStateException { public InvalidSequenceNumberException(long expectedSeqNr, long gotSeqNr, string message) { } + protected InvalidSequenceNumberException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public long ExpectedSeqNr { get; } public long GotSeqNr { get; } } @@ -783,6 +790,7 @@ namespace Akka.Streams public class MaterializationException : System.Exception { public MaterializationException(string message, System.Exception innerException) { } + protected MaterializationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class NoMaterializer : Akka.Streams.IMaterializer { @@ -797,6 +805,7 @@ namespace Akka.Streams public class NoSuchElementException : System.Exception { public NoSuchElementException(string message) { } + protected NoSuchElementException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public abstract class OutPort { @@ -848,8 +857,9 @@ namespace Akka.Streams public sealed class RemoteStreamRefActorTerminatedException : System.Exception { public RemoteStreamRefActorTerminatedException(string message) { } + protected RemoteStreamRefActorTerminatedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public abstract class Shape + public abstract class Shape : System.ICloneable { protected Shape() { } public abstract System.Collections.Immutable.ImmutableArray Inlets { get; } @@ -890,9 +900,14 @@ namespace Akka.Streams public override bool Equals(object obj) { } public override int GetHashCode() { } } + public class StreamDetachedException : System.Exception + { + public static readonly Akka.Streams.StreamDetachedException Instance; + } public class StreamLimitReachedException : System.Exception { public StreamLimitReachedException(long max) { } + protected StreamLimitReachedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static StreamRefAttributes { @@ -941,6 +956,7 @@ namespace Akka.Streams public sealed class StreamRefSubscriptionTimeoutException : Akka.Pattern.IllegalStateException { public StreamRefSubscriptionTimeoutException(string message) { } + protected StreamRefSubscriptionTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public sealed class StreamSubscriptionTimeoutSettings : System.IEquatable { @@ -963,6 +979,7 @@ namespace Akka.Streams { public StreamTcpException(string message) { } public StreamTcpException(string message, System.Exception innerException) { } + protected StreamTcpException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public enum SubstreamCancelStrategy { @@ -972,6 +989,7 @@ namespace Akka.Streams public sealed class TargetRefNotInitializedYetException : Akka.Pattern.IllegalStateException { public TargetRefNotInitializedYetException() { } + protected TargetRefNotInitializedYetException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public enum ThrottleMode { @@ -1017,6 +1035,7 @@ namespace Akka.Streams public class WatchedActorTerminatedException : Akka.Actor.AkkaException { public WatchedActorTerminatedException(string stageName, Akka.Actor.IActorRef actorRef) { } + protected WatchedActorTerminatedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } namespace Akka.Streams.Actors @@ -1454,6 +1473,7 @@ namespace Akka.Streams.Dsl public class FramingException : System.Exception { public FramingException(string message) { } + protected FramingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } public class static GraphDsl @@ -1688,6 +1708,7 @@ namespace Akka.Streams.Dsl public class OutputTruncationException : System.Exception { public OutputTruncationException() { } + protected OutputTruncationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class static PagedSource { @@ -1718,6 +1739,7 @@ namespace Akka.Streams.Dsl public sealed class PartitionOutOfBoundsException : System.Exception { public PartitionOutOfBoundsException(string message) { } + protected PartitionOutOfBoundsException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class PartitionWith : Akka.Streams.Stage.GraphStage> { @@ -2205,6 +2227,7 @@ namespace Akka.Streams.Dsl public sealed class TcpIdleTimeoutException : System.TimeoutException { public TcpIdleTimeoutException(string message, System.TimeSpan duration) { } + protected TcpIdleTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public System.TimeSpan Duration { get; } } public class static TcpStreamExtensions @@ -2219,6 +2242,7 @@ namespace Akka.Streams.Dsl public class UnexpectedOutputException : System.Exception { public UnexpectedOutputException(object element) { } + protected UnexpectedOutputException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class UnzipWith { @@ -3160,6 +3184,7 @@ namespace Akka.Streams.Implementation public class MaterializationPanicException : System.Exception { public MaterializationPanicException(System.Exception innerException) { } + protected MaterializationPanicException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } } [Akka.Annotations.InternalApiAttribute()] @@ -3218,10 +3243,12 @@ namespace Akka.Streams.Implementation public class NormalShutdownException : Akka.Pattern.IllegalStateException { public NormalShutdownException(string message) { } + protected NormalShutdownException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class NothingToReadException : System.Exception { public static readonly Akka.Streams.Implementation.NothingToReadException Instance; + protected NothingToReadException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class OutputBunch { @@ -3382,6 +3409,7 @@ namespace Akka.Streams.Implementation public class SignalThrewException : Akka.Pattern.IllegalStateException, Akka.Streams.Implementation.ISpecViolation { public SignalThrewException(string message, System.Exception cause) { } + protected SignalThrewException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public class SimpleOutputs { @@ -3580,6 +3608,7 @@ namespace Akka.Streams.Implementation { public SubscriptionTimeoutException(string message) { } public SubscriptionTimeoutException(string message, System.Exception innerException) { } + protected SubscriptionTimeoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } [Akka.Annotations.InternalApiAttribute()] public class Throttle : Akka.Streams.Implementation.Fusing.SimpleLinearGraphStage @@ -4838,6 +4867,7 @@ namespace Akka.Streams.Stage public class StageActorRefNotInitializedException : System.Exception { public static readonly Akka.Streams.Stage.StageActorRefNotInitializedException Instance; + protected StageActorRefNotInitializedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } public abstract class StageState { diff --git a/src/core/Akka.Cluster.TestKit/Akka.Cluster.TestKit.csproj b/src/core/Akka.Cluster.TestKit/Akka.Cluster.TestKit.csproj index 79e457c9e33..5325de65dda 100644 --- a/src/core/Akka.Cluster.TestKit/Akka.Cluster.TestKit.csproj +++ b/src/core/Akka.Cluster.TestKit/Akka.Cluster.TestKit.csproj @@ -14,10 +14,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Cluster.Tests.MultiNode/Akka.Cluster.Tests.MultiNode.csproj b/src/core/Akka.Cluster.Tests.MultiNode/Akka.Cluster.Tests.MultiNode.csproj index e1488e9f55b..0635ef67d3f 100644 --- a/src/core/Akka.Cluster.Tests.MultiNode/Akka.Cluster.Tests.MultiNode.csproj +++ b/src/core/Akka.Cluster.Tests.MultiNode/Akka.Cluster.Tests.MultiNode.csproj @@ -23,10 +23,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Cluster.Tests/Akka.Cluster.Tests.csproj b/src/core/Akka.Cluster.Tests/Akka.Cluster.Tests.csproj index 450b6e36d88..ed7152f6bb1 100644 --- a/src/core/Akka.Cluster.Tests/Akka.Cluster.Tests.csproj +++ b/src/core/Akka.Cluster.Tests/Akka.Cluster.Tests.csproj @@ -22,14 +22,6 @@ - - $(DefineConstants);SERIALIZABLE; - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Cluster/Akka.Cluster.csproj b/src/core/Akka.Cluster/Akka.Cluster.csproj index d0f36dd09af..de08aa79f62 100644 --- a/src/core/Akka.Cluster/Akka.Cluster.csproj +++ b/src/core/Akka.Cluster/Akka.Cluster.csproj @@ -15,10 +15,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Cluster/Cluster.cs b/src/core/Akka.Cluster/Cluster.cs index a246c163519..973657b88ae 100644 --- a/src/core/Akka.Cluster/Cluster.cs +++ b/src/core/Akka.Cluster/Cluster.cs @@ -10,6 +10,7 @@ using System.Collections.Immutable; using System.Linq; using System.Reflection; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Akka.Actor; @@ -715,6 +716,16 @@ public class ClusterJoinFailedException : AkkaException public ClusterJoinFailedException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected ClusterJoinFailedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/core/Akka.Cluster/ClusterRemoteWatcher.cs b/src/core/Akka.Cluster/ClusterRemoteWatcher.cs index 0bdb8484158..630c59f2429 100644 --- a/src/core/Akka.Cluster/ClusterRemoteWatcher.cs +++ b/src/core/Akka.Cluster/ClusterRemoteWatcher.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Linq; using Akka.Actor; +using Akka.Dispatch; using Akka.Remote; namespace Akka.Cluster @@ -31,19 +32,21 @@ internal class ClusterRemoteWatcher : RemoteWatcher /// TBD /// TBD /// TBD - public static Props Props( + public new static Props Props( IFailureDetectorRegistry
failureDetector, TimeSpan heartbeatInterval, TimeSpan unreachableReaperInterval, TimeSpan heartbeatExpectedResponseAfter) { return new Props(typeof(ClusterRemoteWatcher), new object[] - { - failureDetector, - heartbeatInterval, - unreachableReaperInterval, - heartbeatExpectedResponseAfter - }).WithDeploy(Deploy.Local); + { + failureDetector, + heartbeatInterval, + unreachableReaperInterval, + heartbeatExpectedResponseAfter + }) + .WithDispatcher(Dispatchers.InternalDispatcherId) + .WithDeploy(Deploy.Local); } private readonly Cluster _cluster; diff --git a/src/core/Akka.Coordination.Tests/Akka.Coordination.Tests.csproj b/src/core/Akka.Coordination.Tests/Akka.Coordination.Tests.csproj index 376da941fd6..21a0b770c56 100644 --- a/src/core/Akka.Coordination.Tests/Akka.Coordination.Tests.csproj +++ b/src/core/Akka.Coordination.Tests/Akka.Coordination.Tests.csproj @@ -20,14 +20,6 @@ - - $(DefineConstants);SERIALIZABLE; - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Coordination/Akka.Coordination.csproj b/src/core/Akka.Coordination/Akka.Coordination.csproj index c6bef8940a3..60fc6c4a9d2 100644 --- a/src/core/Akka.Coordination/Akka.Coordination.csproj +++ b/src/core/Akka.Coordination/Akka.Coordination.csproj @@ -9,10 +9,6 @@ true - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Coordination/LeaseException.cs b/src/core/Akka.Coordination/LeaseException.cs index 153c8fc25ef..29dec0b4c2a 100644 --- a/src/core/Akka.Coordination/LeaseException.cs +++ b/src/core/Akka.Coordination/LeaseException.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; namespace Akka.Coordination { @@ -32,12 +33,10 @@ public LeaseException(string message, Exception innerEx) { } -#if SERIALIZATION protected LeaseException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Coordination/LeaseTimeoutException.cs b/src/core/Akka.Coordination/LeaseTimeoutException.cs index b86f7f0aee7..911ae3e6c8e 100644 --- a/src/core/Akka.Coordination/LeaseTimeoutException.cs +++ b/src/core/Akka.Coordination/LeaseTimeoutException.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; namespace Akka.Coordination { @@ -32,12 +33,10 @@ public LeaseTimeoutException(string message, Exception innerEx) { } -#if SERIALIZATION protected LeaseTimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Discovery/Akka.Discovery.csproj b/src/core/Akka.Discovery/Akka.Discovery.csproj index 0538dc05d49..b1506fc1ef7 100644 --- a/src/core/Akka.Discovery/Akka.Discovery.csproj +++ b/src/core/Akka.Discovery/Akka.Discovery.csproj @@ -23,10 +23,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj b/src/core/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj index 2202d5e4c4f..52925f37da8 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj +++ b/src/core/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj @@ -27,10 +27,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj b/src/core/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj index bff83b1d9b2..ea0d22b714a 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj @@ -33,10 +33,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs b/src/core/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs index 86a5f50ef22..9e6889d5467 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Reflection; -#if CORECLR namespace System.CodeDom.Compiler { public class CompilerErrorCollection : List @@ -22,4 +21,3 @@ public class CompilerError public bool IsWarning { get; set; } } } -#endif diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs index a149c39075a..981d7ada914 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs @@ -12,7 +12,6 @@ using System.Text; using System.Threading.Tasks; -#if CORECLR namespace Akka.MultiNodeTestRunner.Shared.Extensions { internal static class DateTimeExtension @@ -23,4 +22,3 @@ public static string ToShortTimeString(this DateTime dateTime) } } } -#endif diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs index ca6be97f546..6b0b371ff30 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs @@ -12,7 +12,6 @@ using System.Text; using System.Threading.Tasks; -#if CORECLR namespace Akka.MultiNodeTestRunner.Shared.Extensions { internal static class TypeExtension @@ -23,4 +22,3 @@ public static MethodInfo GetMethod(this Type type, string method, params Type[] } } } -#endif diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs index 8edb367beab..ed078b136b9 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs @@ -15,10 +15,6 @@ // // ------------------------------------------------------------------------------ -#if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; -#endif - namespace Akka.MultiNodeTestRunner.Shared.Persistence { using System.Linq; diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs index 22b92aa1f4c..e2a7c0c7877 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs @@ -9,9 +9,6 @@ using System.Linq; using Akka.Actor; using Akka.Event; -#if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; -#endif using Akka.MultiNodeTestRunner.Shared.Reporting; namespace Akka.MultiNodeTestRunner.Shared.Sinks diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs index 1f43f991968..3b9587807a2 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs @@ -187,11 +187,7 @@ private void PublishToChildren(RunnerMessage message) { foreach (var sink in Sinks) { -#if CORECLR - sink.LogRunnerMessage(message.Message, Assembly.GetEntryAssembly().GetName().Name, LogLevel.InfoLevel); -#else sink.LogRunnerMessage(message.Message, Assembly.GetExecutingAssembly().GetName().Name, LogLevel.InfoLevel); -#endif } } diff --git a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs index 633f81266df..7280cef69aa 100644 --- a/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs +++ b/src/core/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs @@ -13,9 +13,6 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -#if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; -#endif using Akka.MultiNodeTestRunner.Shared.Reporting; using JetBrains.TeamCity.ServiceMessages; using JetBrains.TeamCity.ServiceMessages.Write.Special; diff --git a/src/core/Akka.MultiNodeTestRunner/Discovery.cs b/src/core/Akka.MultiNodeTestRunner/Discovery.cs index 8d6a88cc339..c45bed128b0 100644 --- a/src/core/Akka.MultiNodeTestRunner/Discovery.cs +++ b/src/core/Akka.MultiNodeTestRunner/Discovery.cs @@ -20,11 +20,7 @@ namespace Akka.MultiNodeTestRunner { -#if CORECLR - public class Discovery : IMessageSink, IDisposable -#else public class Discovery : MarshalByRefObject, IMessageSink, IDisposable -#endif { public Dictionary> Tests { get; set; } public List Errors { get; } = new List(); @@ -75,12 +71,8 @@ private List LoadTestCaseDetails(ITestCaseDiscoveryMessage testCaseDis { try { -#if CORECLR - var specType = testCaseDiscoveryMessage.TestAssembly.Assembly.GetType(testClass.Name).ToRuntimeType(); -#else var testAssembly = Assembly.LoadFrom(testCaseDiscoveryMessage.TestAssembly.Assembly.AssemblyPath); var specType = testAssembly.GetType(testClass.Name); -#endif var roles = RoleNames(specType); var details = roles.Select((r, i) => new NodeTest @@ -118,20 +110,11 @@ internal static ConstructorInfo FindConfigConstructor(Type configUser) var current = configUser; while (current != null) { - -#if CORECLR - var ctorWithConfig = current - .GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) - .FirstOrDefault(c => null != c.GetParameters().FirstOrDefault(p => p.ParameterType.GetTypeInfo().IsSubclassOf(baseConfigType))); - - current = current.GetTypeInfo().BaseType; -#else var ctorWithConfig = current .GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .FirstOrDefault(c => null != c.GetParameters().FirstOrDefault(p => p.ParameterType.IsSubclassOf(baseConfigType))); current = current.BaseType; -#endif if (ctorWithConfig != null) return ctorWithConfig; } @@ -143,15 +126,9 @@ private object[] ConfigConstructorParamValues(Type configType) var ctors = configType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); var empty = ctors.FirstOrDefault(c => !c.GetParameters().Any()); -#if CORECLR - return empty != null - ? new object[0] - : ctors.First().GetParameters().Select(p => p.ParameterType.GetTypeInfo().IsValueType ? Activator.CreateInstance(p.ParameterType) : null).ToArray(); -#else return empty != null ? new object[0] : ctors.First().GetParameters().Select(p => p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null).ToArray(); -#endif } /// diff --git a/src/core/Akka.Persistence.Query.Tests/Akka.Persistence.Query.Tests.csproj b/src/core/Akka.Persistence.Query.Tests/Akka.Persistence.Query.Tests.csproj index 18660d72b06..087a19a6c01 100644 --- a/src/core/Akka.Persistence.Query.Tests/Akka.Persistence.Query.Tests.csproj +++ b/src/core/Akka.Persistence.Query.Tests/Akka.Persistence.Query.Tests.csproj @@ -23,14 +23,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence.TCK.Tests/Akka.Persistence.TCK.Tests.csproj b/src/core/Akka.Persistence.TCK.Tests/Akka.Persistence.TCK.Tests.csproj index 2906e799e2c..6ccfccb8b8a 100644 --- a/src/core/Akka.Persistence.TCK.Tests/Akka.Persistence.TCK.Tests.csproj +++ b/src/core/Akka.Persistence.TCK.Tests/Akka.Persistence.TCK.Tests.csproj @@ -18,14 +18,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence.TCK/Akka.Persistence.TCK.csproj b/src/core/Akka.Persistence.TCK/Akka.Persistence.TCK.csproj index a8660db1231..20630676aae 100644 --- a/src/core/Akka.Persistence.TCK/Akka.Persistence.TCK.csproj +++ b/src/core/Akka.Persistence.TCK/Akka.Persistence.TCK.csproj @@ -20,14 +20,6 @@ - - $(DefineConstants);SERIALIZATION - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence.TCK/Journal/JournalSpec.cs b/src/core/Akka.Persistence.TCK/Journal/JournalSpec.cs index 10248194248..c2d5a0d6237 100644 --- a/src/core/Akka.Persistence.TCK/Journal/JournalSpec.cs +++ b/src/core/Akka.Persistence.TCK/Journal/JournalSpec.cs @@ -9,10 +9,12 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.Serialization; using Akka.Actor; using Akka.Actor.Setup; using Akka.Configuration; using Akka.Persistence.TCK.Serialization; +using Akka.Serialization; using Akka.TestKit; using Xunit; using Xunit.Abstractions; @@ -324,7 +326,6 @@ public void Journal_should_serialize_events() Assertions.AssertEqual(_receiverProbe.ExpectMsg().HighestSequenceNr, 6L); } -#if !CORECLR /// /// JSON serializer should fail on this /// @@ -343,6 +344,35 @@ public void Journal_optionally_may_reject_non_serializable_events() { if (!SupportsRejectingNonSerializableObjects) return; + // Test that JSON actually fail + var serializer = Sys.Serialization.FindSerializerForType(typeof(NotSerializableEvent)); + if (!(serializer is NewtonSoftJsonSerializer)) + { + Output.WriteLine("[SKIP] This test only works with NewtonSoftJsonSerializer."); + return; + } + + var serializerFailed = false; + try + { + var serialized = serializer.ToBinary(new NotSerializableEvent(true)); + var deserialized = serializer.FromBinary(serialized); + if (!(deserialized is NotSerializableEvent)) + throw new Exception(); + } + catch (Exception) + { + serializerFailed = true; + } + + if (!serializerFailed) + { + Output.WriteLine("[SKIP] This test assumes that the serializer will fail, but it doesn't."); + return; + } + // End test + + // Start of actual test var msgs = Enumerable.Range(6, 3).Select(i => { var evt = i == 7 ? (object) new NotSerializableEvent(false) : "b-" + i; @@ -371,6 +401,5 @@ public void Journal_optionally_may_reject_non_serializable_events() m.Persistent.WriterGuid.Equals(writerGuid) && m.Persistent.Payload.Equals("b-8")); } -#endif } } diff --git a/src/core/Akka.Persistence.TestKit.Tests/Akka.Persistence.TestKit.Tests.csproj b/src/core/Akka.Persistence.TestKit.Tests/Akka.Persistence.TestKit.Tests.csproj index f85e0a212d3..904e599ab7e 100644 --- a/src/core/Akka.Persistence.TestKit.Tests/Akka.Persistence.TestKit.Tests.csproj +++ b/src/core/Akka.Persistence.TestKit.Tests/Akka.Persistence.TestKit.Tests.csproj @@ -23,10 +23,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence.Tests/Akka.Persistence.Tests.csproj b/src/core/Akka.Persistence.Tests/Akka.Persistence.Tests.csproj index 6d8d97a6971..c13d632210c 100644 --- a/src/core/Akka.Persistence.Tests/Akka.Persistence.Tests.csproj +++ b/src/core/Akka.Persistence.Tests/Akka.Persistence.Tests.csproj @@ -25,14 +25,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence/Akka.Persistence.csproj b/src/core/Akka.Persistence/Akka.Persistence.csproj index 2ca58072544..24509b35af6 100644 --- a/src/core/Akka.Persistence/Akka.Persistence.csproj +++ b/src/core/Akka.Persistence/Akka.Persistence.csproj @@ -14,9 +14,6 @@ - - $(DefineConstants);CORECLR - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Persistence/AtLeastOnceDeliverySemantic.cs b/src/core/Akka.Persistence/AtLeastOnceDeliverySemantic.cs index 37725cccf39..cd919ab6cec 100644 --- a/src/core/Akka.Persistence/AtLeastOnceDeliverySemantic.cs +++ b/src/core/Akka.Persistence/AtLeastOnceDeliverySemantic.cs @@ -225,7 +225,6 @@ public MaxUnconfirmedMessagesExceededException(string message, Exception innerEx { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -234,7 +233,6 @@ public MaxUnconfirmedMessagesExceededException(string message, Exception innerEx protected MaxUnconfirmedMessagesExceededException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } #endregion diff --git a/src/core/Akka.Persistence/Journal/AsyncWriteProxy.cs b/src/core/Akka.Persistence/Journal/AsyncWriteProxy.cs index 920670a9a94..ba4bea1f8d5 100644 --- a/src/core/Akka.Persistence/Journal/AsyncWriteProxy.cs +++ b/src/core/Akka.Persistence/Journal/AsyncWriteProxy.cs @@ -37,7 +37,6 @@ public AsyncReplayTimeoutException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -47,7 +46,6 @@ protected AsyncReplayTimeoutException(SerializationInfo info, StreamingContext c : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Persistence/PersistentActor.cs b/src/core/Akka.Persistence/PersistentActor.cs index 37798bd47a9..66c86ba07b6 100644 --- a/src/core/Akka.Persistence/PersistentActor.cs +++ b/src/core/Akka.Persistence/PersistentActor.cs @@ -136,7 +136,6 @@ public RecoveryTimedOutException(string message, Exception cause = null) : base( { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -145,7 +144,6 @@ public RecoveryTimedOutException(string message, Exception cause = null) : base( public RecoveryTimedOutException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Persistence/Snapshot/NoSnapshotStore.cs b/src/core/Akka.Persistence/Snapshot/NoSnapshotStore.cs index 7e448c8b619..cc26c7ea503 100644 --- a/src/core/Akka.Persistence/Snapshot/NoSnapshotStore.cs +++ b/src/core/Akka.Persistence/Snapshot/NoSnapshotStore.cs @@ -49,7 +49,6 @@ public NoSnapshotStoreException(string message, Exception innerException) : base { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -58,7 +57,6 @@ public NoSnapshotStoreException(string message, Exception innerException) : base protected NoSnapshotStoreException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Remote.TestKit.Tests/Akka.Remote.TestKit.Tests.csproj b/src/core/Akka.Remote.TestKit.Tests/Akka.Remote.TestKit.Tests.csproj index 432597cd86a..85ff907b309 100644 --- a/src/core/Akka.Remote.TestKit.Tests/Akka.Remote.TestKit.Tests.csproj +++ b/src/core/Akka.Remote.TestKit.Tests/Akka.Remote.TestKit.Tests.csproj @@ -23,14 +23,6 @@ - - $(DefineConstants);SERIALIZABLE - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Remote.TestKit/Akka.Remote.TestKit.csproj b/src/core/Akka.Remote.TestKit/Akka.Remote.TestKit.csproj index c81855c34ed..d19e3b81d57 100644 --- a/src/core/Akka.Remote.TestKit/Akka.Remote.TestKit.csproj +++ b/src/core/Akka.Remote.TestKit/Akka.Remote.TestKit.csproj @@ -22,11 +22,7 @@ - - - $(DefineConstants);CORECLR - - + $(DefineConstants);RELEASE diff --git a/src/core/Akka.Remote.TestKit/Controller.cs b/src/core/Akka.Remote.TestKit/Controller.cs index ef7ced008dc..2954d07e35b 100644 --- a/src/core/Akka.Remote.TestKit/Controller.cs +++ b/src/core/Akka.Remote.TestKit/Controller.cs @@ -98,7 +98,6 @@ public class ClientDisconnectedException : AkkaException /// The message that describes the error. public ClientDisconnectedException(string message) : base(message){} -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -107,7 +106,6 @@ public ClientDisconnectedException(string message) : base(message){} protected ClientDisconnectedException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } public class GetNodes diff --git a/src/core/Akka.Remote.TestKit/Internals/TestConductorConfigFactory.cs b/src/core/Akka.Remote.TestKit/Internals/TestConductorConfigFactory.cs index 19bce8cabe4..e880801c02d 100644 --- a/src/core/Akka.Remote.TestKit/Internals/TestConductorConfigFactory.cs +++ b/src/core/Akka.Remote.TestKit/Internals/TestConductorConfigFactory.cs @@ -36,11 +36,7 @@ public static Config Default() /// The configuration defined in the current executing assembly. internal static Config FromResource(string resourceName) { -#if CORECLR - var assembly = typeof(TestConductorConfigFactory).GetTypeInfo().Assembly; -#else var assembly = typeof(TestConductorConfigFactory).Assembly; -#endif using (var stream = assembly.GetManifestResourceStream(resourceName)) { diff --git a/src/core/Akka.Remote.TestKit/MultiNodeSpec.cs b/src/core/Akka.Remote.TestKit/MultiNodeSpec.cs index d6ef4b7cca5..9e4c97549e4 100644 --- a/src/core/Akka.Remote.TestKit/MultiNodeSpec.cs +++ b/src/core/Akka.Remote.TestKit/MultiNodeSpec.cs @@ -408,13 +408,7 @@ protected MultiNodeSpec( _roles = roles; _deployments = deployments; -#if CORECLR - var dnsTask = Dns.GetHostAddressesAsync(ServerName); - dnsTask.Wait(); - var node = new IPEndPoint(dnsTask.Result[0], ServerPort); -#else var node = new IPEndPoint(Dns.GetHostAddresses(ServerName)[0], ServerPort); -#endif _controllerAddr = node; AttachConductor(new TestConductor(system)); diff --git a/src/core/Akka.Remote.Tests.MultiNode/Akka.Remote.Tests.MultiNode.csproj b/src/core/Akka.Remote.Tests.MultiNode/Akka.Remote.Tests.MultiNode.csproj index b5c14a11e32..aa0d86570ea 100644 --- a/src/core/Akka.Remote.Tests.MultiNode/Akka.Remote.Tests.MultiNode.csproj +++ b/src/core/Akka.Remote.Tests.MultiNode/Akka.Remote.Tests.MultiNode.csproj @@ -27,10 +27,6 @@ $(DefineConstants); - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Remote.Tests/ExceptionSupportSpec.cs b/src/core/Akka.Remote.Tests/ExceptionSupportSpec.cs new file mode 100644 index 00000000000..bac4420101f --- /dev/null +++ b/src/core/Akka.Remote.Tests/ExceptionSupportSpec.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Configuration; +using Akka.Dispatch; +using Akka.IO.Buffers; +using Akka.Pattern; +using Akka.Remote.Serialization; +using Akka.Remote.Transport; +using Akka.Serialization; +using Akka.TestKit; +using Xunit; +using Xunit.Abstractions; +using FluentAssertions; + +namespace Akka.Remote.Tests +{ + public class ExceptionSupportSpec : AkkaSpec + { + private readonly ExceptionSupport _serializer; + private readonly Exception _innerException = new Exception("inner message"); + private readonly Exception _innerException2 = new Exception("inner message 2"); + + public ExceptionSupportSpec(ITestOutputHelper output) : base(output) + { + _serializer = new ExceptionSupport((ExtendedActorSystem)Sys); + } + + [Theory] + [InlineData(new object[]{typeof(ActorInterruptedException)})] + [InlineData(new object[]{typeof(ActorNotFoundException)})] + [InlineData(new object[]{typeof(InvalidActorNameException)})] + [InlineData(new object[]{typeof(LoggerInitializationException)})] + [InlineData(new object[]{typeof(StashOverflowException)})] + [InlineData(new object[]{typeof(ConfigurationException)})] + [InlineData(new object[]{typeof(RejectedExecutionException)})] + [InlineData(new object[]{typeof(IllegalStateException)})] + [InlineData(new object[]{typeof(RemoteTransportException)})] + [InlineData(new object[]{typeof(AkkaProtocolException)})] + [InlineData(new object[]{typeof(InvalidAssociationException)})] + public void ExceptionSupport_should_serialize_exceptions_with_inner_exception(Type type) + { + var instance = (Exception) Activator.CreateInstance(type, "TestMessage", _innerException); + AssertDefaultsEquals(instance); + } + + [Theory] + [InlineData(new object[]{typeof(ActorKilledException)})] + [InlineData(new object[]{typeof(AskTimeoutException)})] + [InlineData(new object[]{typeof(IllegalActorNameException)})] + [InlineData(new object[]{typeof(IllegalActorStateException)})] + [InlineData(new object[]{typeof(InvalidMessageException)})] + [InlineData(new object[]{typeof(SchedulerException)})] + [InlineData(new object[]{typeof(BufferPoolAllocationException)})] + public void ExceptionSupport_should_serialize_exceptions_with_message(Type type) + { + var instance = (Exception) Activator.CreateInstance(type, "TestMessage"); + AssertDefaultsEquals(instance); + } + + [Theory] + [InlineData(new object[]{typeof(UserCalledFailException)})] + public void ExceptionSupport_should_serialize_exceptions(Type type) + { + var instance = (Exception) Activator.CreateInstance(type, new object[]{}); + AssertDefaultsEquals(instance); + } + + [Fact] + public void ExceptionSupport_should_serialize_ActorInitializationException() + { + var probe = CreateTestProbe(); + var exception = AssertDefaultsEquals(new ActorInitializationException(probe.Ref, "TestMessage", _innerException)); + + exception.Actor.Should().NotBeNull(); + exception.Actor.Equals(probe).Should().BeTrue(); + } + + [Fact] + public void ExceptionSupport_should_serialize_DeathPactException() + { + var probe = CreateTestProbe(); + var exception = AssertDefaultsEquals(new DeathPactException(probe.Ref)); + + exception.DeadActor.Should().NotBeNull(); + exception.DeadActor.Equals(probe).Should().BeTrue(); + } + + [Fact] + public void ExceptionSupport_should_serialize_PostRestartException() + { + var probe = CreateTestProbe(); + var exception = AssertDefaultsEquals(new PostRestartException(probe.Ref, _innerException, _innerException2)); + + exception.Actor.Should().NotBeNull(); + exception.Actor.Equals(probe).Should().BeTrue(); + AssertExceptionEquals(_innerException2, exception.OriginalCause); + } + + [Fact] + public void ExceptionSupport_should_serialize_PreRestartException() + { + var probe = CreateTestProbe(); + var testMessage = new + { + value = 1 + }; + var exception = AssertDefaultsEquals(new PreRestartException(probe.Ref, _innerException2, _innerException, testMessage)); + + exception.Actor.Should().NotBeNull(); + exception.Actor.Equals(probe).Should().BeTrue(); + AssertExceptionEquals(_innerException2, exception.RestartException); + exception.OptionalMessage.Should().BeEquivalentTo(testMessage); + } + + [Fact] + public void ExceptionSupport_should_serialize_OpenCircuitException() + { + var remaining = new TimeSpan(1234567); + var exception = AssertDefaultsEquals(new OpenCircuitException("TestMessage", _innerException, remaining)); + + exception.RemainingDuration.Should().Be(remaining); + } + + private T AssertDefaultsEquals(T expected) where T: Exception + { + var serialized = _serializer.ExceptionToProto(expected); + var deserialized = (T)_serializer.ExceptionFromProto(serialized); + + AssertExceptionEquals(expected, deserialized); + + return deserialized; + } + + private void AssertExceptionEquals(Exception expected, Exception actual) + { + actual.Message.Should().Be(expected.Message); + // HResult is not serialized + // actual.HResult.Should().Be(expected.HResult); + actual.Source.Should().Be(expected.Source); + actual.StackTrace.Should().Be(expected.StackTrace); + actual.TargetSite.Should().BeEquivalentTo(expected.TargetSite); + if (actual.InnerException != null) + { + AssertExceptionEquals(actual.InnerException, expected.InnerException); + } + } + } +} diff --git a/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs b/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs index aa8fe99a79d..2cc9bbd4561 100644 --- a/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs +++ b/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs @@ -8,6 +8,7 @@ using Akka.Configuration; using Akka.Remote.Configuration; using Akka.Remote.Serialization; +using Akka.Serialization; using Akka.TestKit; using FluentAssertions; using Xunit; @@ -57,16 +58,29 @@ public void Can_serialize_String(string value) private T AssertAndReturn(T message) { - var serializer = Sys.Serialization.FindSerializerFor(message); + var serializer = (SerializerWithStringManifest)Sys.Serialization.FindSerializerFor(message); serializer.Should().BeOfType(); var serializedBytes = serializer.ToBinary(message); - return (T)serializer.FromBinary(serializedBytes, typeof(T)); + var manifest = serializer.Manifest(message); + return (T)serializer.FromBinary(serializedBytes, manifest); + } + + private T AssertCrossPlatformAndReturn(T message) + { + var serializer = (SerializerWithStringManifest)Sys.Serialization.FindSerializerFor(message); + serializer.Should().BeOfType(); + var serializedBytes = serializer.ToBinary(message); + // GetType() will make sure that each namespace is compatible with the serializer + // as the test is run on each platform. + return (T)serializer.FromBinary(serializedBytes, message.GetType()); } private void AssertEqual(T message) { var deserialized = AssertAndReturn(message); Assert.Equal(message, deserialized); + deserialized = AssertCrossPlatformAndReturn(message); + Assert.Equal(message, deserialized); } } } diff --git a/src/core/Akka.Remote/AckedDelivery.cs b/src/core/Akka.Remote/AckedDelivery.cs index bdd1ba1d359..e9ad344d36e 100644 --- a/src/core/Akka.Remote/AckedDelivery.cs +++ b/src/core/Akka.Remote/AckedDelivery.cs @@ -341,7 +341,6 @@ public ResendBufferCapacityReachedException(int c) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -351,7 +350,6 @@ protected ResendBufferCapacityReachedException(SerializationInfo info, Streaming : base(info, context) { } -#endif } /// @@ -365,6 +363,17 @@ internal class ResendUnfulfillableException : AkkaException public ResendUnfulfillableException() : base("Unable to fulfill resend request since negatively acknowledged payload is no longer in buffer. " + "The resend states between two systems are compromised and cannot be recovered") { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected ResendUnfulfillableException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } #endregion diff --git a/src/core/Akka.Remote/Akka.Remote.csproj b/src/core/Akka.Remote/Akka.Remote.csproj index 24f27dca6f8..5b1f41f9715 100644 --- a/src/core/Akka.Remote/Akka.Remote.csproj +++ b/src/core/Akka.Remote/Akka.Remote.csproj @@ -15,9 +15,6 @@ - - $(DefineConstants);CORECLR - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Remote/Endpoint.cs b/src/core/Akka.Remote/Endpoint.cs index 77ee4275174..607eef5e67d 100644 --- a/src/core/Akka.Remote/Endpoint.cs +++ b/src/core/Akka.Remote/Endpoint.cs @@ -191,7 +191,6 @@ internal class EndpointException : AkkaException /// The exception that is the cause of the current exception. public EndpointException(string message, Exception cause = null) : base(message, cause) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -201,7 +200,6 @@ protected EndpointException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// @@ -325,6 +323,16 @@ public EndpointDisassociatedException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected EndpointDisassociatedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } /// @@ -347,6 +355,16 @@ public EndpointAssociationException(string message) /// The message that describes the error. /// The exception that is the cause of the current exception. public EndpointAssociationException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected EndpointAssociationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } /// @@ -362,6 +380,16 @@ public OversizedPayloadException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected OversizedPayloadException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } #endregion diff --git a/src/core/Akka.Remote/RemoteTransport.cs b/src/core/Akka.Remote/RemoteTransport.cs index d3243b21327..2798544a919 100644 --- a/src/core/Akka.Remote/RemoteTransport.cs +++ b/src/core/Akka.Remote/RemoteTransport.cs @@ -132,7 +132,6 @@ public RemoteTransportException(string message, Exception cause = null) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -142,7 +141,6 @@ protected RemoteTransportException(SerializationInfo info, StreamingContext cont : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Remote/RemoteWatcher.cs b/src/core/Akka.Remote/RemoteWatcher.cs index ce8a8e81231..67ed6d7e631 100644 --- a/src/core/Akka.Remote/RemoteWatcher.cs +++ b/src/core/Akka.Remote/RemoteWatcher.cs @@ -54,6 +54,7 @@ public static Props Props( TimeSpan heartbeatExpectedResponseAfter) { return Actor.Props.Create(() => new RemoteWatcher(failureDetector, heartbeatInterval, unreachableReaperInterval, heartbeatExpectedResponseAfter)) + .WithDispatcher(Dispatchers.InternalDispatcherId) .WithDeploy(Deploy.Local); } diff --git a/src/core/Akka.Remote/Serialization/ExceptionSupport.cs b/src/core/Akka.Remote/Serialization/ExceptionSupport.cs index ca221573d2f..52e6a2b89ab 100644 --- a/src/core/Akka.Remote/Serialization/ExceptionSupport.cs +++ b/src/core/Akka.Remote/Serialization/ExceptionSupport.cs @@ -13,9 +13,8 @@ using Akka.Util; using Akka.Util.Internal; using Google.Protobuf; -#if SERIALIZATION using System.Runtime.Serialization; -#endif +using Akka.Remote.Serialization.Proto.Msg; namespace Akka.Remote.Serialization { @@ -23,7 +22,7 @@ internal class ExceptionSupport { private readonly WrappedPayloadSupport _wrappedPayloadSupport; private const BindingFlags All = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; - private HashSet DefaultProperties = new HashSet + private readonly HashSet _defaultProperties = new HashSet { "ClassName", "Message", @@ -54,11 +53,7 @@ public byte[] SerializeException(Exception exception) internal Proto.Msg.ExceptionData ExceptionToProto(Exception exception) { -#if SERIALIZATION return ExceptionToProtoNet(exception); -#else - return ExceptionToProtoNetCore(exception); -#endif } public Exception DeserializeException(byte[] bytes) @@ -69,15 +64,10 @@ public Exception DeserializeException(byte[] bytes) internal Exception ExceptionFromProto(Proto.Msg.ExceptionData proto) { -#if SERIALIZATION return ExceptionFromProtoNet(proto); -#else - return ExceptionFromProtoNetCore(proto); -#endif } -#if SERIALIZATION - private FormatterConverter DefaultFormatterConverter = new FormatterConverter(); + private readonly FormatterConverter _defaultFormatterConverter = new FormatterConverter(); public Proto.Msg.ExceptionData ExceptionToProtoNet(Exception exception) { @@ -95,14 +85,22 @@ public Proto.Msg.ExceptionData ExceptionToProtoNet(Exception exception) message.InnerException = ExceptionToProto(exception.InnerException); var serializable = exception as ISerializable; - var serializationInfo = new SerializationInfo(exceptionType, DefaultFormatterConverter); + var serializationInfo = new SerializationInfo(exceptionType, _defaultFormatterConverter); serializable.GetObjectData(serializationInfo, new StreamingContext()); foreach (var info in serializationInfo) { - if (DefaultProperties.Contains(info.Name)) continue; - var preparedValue = _wrappedPayloadSupport.PayloadToProto(info.Value); - message.CustomFields.Add(info.Name, preparedValue); + if (_defaultProperties.Contains(info.Name)) continue; + if (info.Value is Exception exceptionValue) + { + var exceptionPayload = ExceptionToProto(exceptionValue); + var preparedValue = _wrappedPayloadSupport.PayloadToProto(exceptionPayload); + message.CustomFields.Add(info.Name, preparedValue); + } else + { + var preparedValue = _wrappedPayloadSupport.PayloadToProto(info.Value); + message.CustomFields.Add(info.Name, preparedValue); + } } return message; @@ -115,22 +113,25 @@ public Exception ExceptionFromProtoNet(Proto.Msg.ExceptionData proto) Type exceptionType = Type.GetType(proto.TypeName); - var serializationInfo = new SerializationInfo(exceptionType, DefaultFormatterConverter); + var serializationInfo = new SerializationInfo(exceptionType, _defaultFormatterConverter); serializationInfo.AddValue("ClassName", proto.TypeName); - serializationInfo.AddValue("Message", proto.Message); - serializationInfo.AddValue("StackTraceString", proto.StackTrace); - serializationInfo.AddValue("Source", proto.Source); + serializationInfo.AddValue("Message", ValueOrNull(proto.Message)); + serializationInfo.AddValue("StackTraceString", ValueOrNull(proto.StackTrace)); + serializationInfo.AddValue("Source", ValueOrNull(proto.Source)); serializationInfo.AddValue("InnerException", ExceptionFromProto(proto.InnerException)); serializationInfo.AddValue("HelpURL", string.Empty); - serializationInfo.AddValue("RemoteStackTraceString", string.Empty); + serializationInfo.AddValue("RemoteStackTraceString", null); serializationInfo.AddValue("RemoteStackIndex", 0); - serializationInfo.AddValue("ExceptionMethod", string.Empty); + serializationInfo.AddValue("ExceptionMethod", null); serializationInfo.AddValue("HResult", int.MinValue); foreach (var field in proto.CustomFields) { - serializationInfo.AddValue(field.Key, _wrappedPayloadSupport.PayloadFrom(field.Value)); + var payload = _wrappedPayloadSupport.PayloadFrom(field.Value); + if (payload is ExceptionData exception) + payload = ExceptionFromProto(exception); + serializationInfo.AddValue(field.Key, payload); } Exception obj = null; @@ -148,71 +149,8 @@ public Exception ExceptionFromProtoNet(Proto.Msg.ExceptionData proto) return obj; } -#else - private TypeInfo ExceptionTypeInfo = typeof(Exception).GetTypeInfo(); - - internal Proto.Msg.ExceptionData ExceptionToProtoNetCore(Exception exception) - { - var message = new Proto.Msg.ExceptionData(); - - if (exception == null) - return message; - - var exceptionType = exception.GetType(); - - message.TypeName = exceptionType.TypeQualifiedName(); - message.Message = exception.Message; - message.StackTrace = exception.StackTrace ?? ""; - message.Source = exception.Source ?? ""; - message.InnerException = ExceptionToProto(exception.InnerException); - - // serialize all public properties - foreach (var property in exceptionType.GetTypeInfo().DeclaredProperties) - { - if (DefaultProperties.Contains(property.Name)) continue; - if (property.SetMethod != null) - { - message.CustomFields.Add(property.Name, _wrappedPayloadSupport.PayloadToProto(property.GetValue(exception))); - } - } - - return message; - } - - internal Exception ExceptionFromProtoNetCore(Proto.Msg.ExceptionData proto) - { - if (string.IsNullOrEmpty(proto.TypeName)) - return null; - - Type exceptionType = Type.GetType(proto.TypeName); - - var obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(exceptionType); - if (!string.IsNullOrEmpty(proto.Message)) - ExceptionTypeInfo?.GetField("_message", All)?.SetValue(obj, proto.Message); - - if (!string.IsNullOrEmpty(proto.StackTrace)) - ExceptionTypeInfo?.GetField("_stackTraceString", All)?.SetValue(obj, proto.StackTrace); - - if (!string.IsNullOrEmpty(proto.Source)) - ExceptionTypeInfo?.GetField("_source", All)?.SetValue(obj, proto.Source); - - if (!string.IsNullOrEmpty(proto.InnerException.TypeName)) - ExceptionTypeInfo?.GetField("_innerException", All)?.SetValue(obj, ExceptionFromProto(proto.InnerException)); - - // deserialize all public properties with setters - foreach (var property in proto.CustomFields) - { - if (DefaultProperties.Contains(property.Key)) continue; - var prop = exceptionType.GetProperty(property.Key, All); - if (prop.SetMethod != null) - { - prop.SetValue(obj, _wrappedPayloadSupport.PayloadFrom(property.Value)); - } - } - - return (Exception)obj; - } -#endif + private string ValueOrNull(string value) + => string.IsNullOrEmpty(value) ? null : value; } } diff --git a/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs b/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs index 21c7a5665fb..f8716c10f36 100644 --- a/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs +++ b/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs @@ -13,8 +13,22 @@ namespace Akka.Remote.Serialization { - public sealed class PrimitiveSerializers : Serializer + public sealed class PrimitiveSerializers : SerializerWithStringManifest { + internal const string StringManifest = "S"; + internal const string Int32Manifest = "I"; + internal const string Int64Manifest = "L"; + + // .Net Core manifests + internal const string StringManifestNetCore = "System.String, System.Private.CoreLib"; + internal const string Int32ManifestNetCore = "System.Int32, System.Private.CoreLib"; + internal const string Int64ManifestNetCore = "System.Int64, System.Private.CoreLib"; + + // .Net Framework manifests + internal const string StringManifestNetFx = "System.String, mscorlib"; + internal const string Int32ManifestNetFx = "System.Int32, mscorlib"; + internal const string Int64ManifestNetFx = "System.Int64, mscorlib"; + /// /// Initializes a new instance of the class. /// @@ -23,28 +37,58 @@ public PrimitiveSerializers(ExtendedActorSystem system) : base(system) { } - /// - public override bool IncludeManifest { get; } = true; - /// public override byte[] ToBinary(object obj) { - var str = obj as string; - if (str != null) return Encoding.UTF8.GetBytes(str); - if (obj is int) return BitConverter.GetBytes((int)obj); - if (obj is long) return BitConverter.GetBytes((long)obj); - - throw new ArgumentException($"Cannot serialize object of type [{obj.GetType().TypeQualifiedName()}]"); + switch (obj) + { + case string s: + return Encoding.UTF8.GetBytes(s); + case int i: + return BitConverter.GetBytes(i); + case long l: + return BitConverter.GetBytes(l); + default: + throw new ArgumentException($"Cannot serialize object of type [{obj.GetType()}]"); + } } /// - public override object FromBinary(byte[] bytes, Type type) + public override object FromBinary(byte[] bytes, string manifest) { - if (type == typeof(string)) return Encoding.UTF8.GetString(bytes); - if (type == typeof(int)) return BitConverter.ToInt32(bytes, 0); - if (type == typeof(long)) return BitConverter.ToInt64(bytes, 0); + switch (manifest) + { + case StringManifest: + case StringManifestNetCore: + case StringManifestNetFx: + return Encoding.UTF8.GetString(bytes); + case Int32Manifest: + case Int32ManifestNetCore: + case Int32ManifestNetFx: + return BitConverter.ToInt32(bytes, 0); + case Int64Manifest: + case Int64ManifestNetCore: + case Int64ManifestNetFx: + return BitConverter.ToInt64(bytes, 0); + default: + throw new ArgumentException($"Unimplemented deserialization of message with manifest [{manifest}] in [${GetType()}]"); + } + } - throw new ArgumentException($"Unimplemented deserialization of message with manifest [{type.TypeQualifiedName()}] in [${nameof(PrimitiveSerializers)}]"); + /// + public override string Manifest(object obj) + { + switch (obj) + { + case string _: + return StringManifest; + case int _: + return Int32Manifest; + case long _: + return Int64Manifest; + default: + throw new ArgumentException($"Cannot serialize object of type [{obj.GetType()}] in [{GetType()}]"); + } } } } diff --git a/src/core/Akka.Remote/Transport/AkkaPduCodec.cs b/src/core/Akka.Remote/Transport/AkkaPduCodec.cs index bba9c376eed..bde6e0baf27 100644 --- a/src/core/Akka.Remote/Transport/AkkaPduCodec.cs +++ b/src/core/Akka.Remote/Transport/AkkaPduCodec.cs @@ -28,7 +28,6 @@ internal class PduCodecException : AkkaException /// The exception that is the cause of the current exception. public PduCodecException(string message, Exception cause = null) : base(message, cause) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -38,7 +37,6 @@ protected PduCodecException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /* diff --git a/src/core/Akka.Remote/Transport/AkkaProtocolTransport.cs b/src/core/Akka.Remote/Transport/AkkaProtocolTransport.cs index 8d00793b9e4..cdb8a62239c 100644 --- a/src/core/Akka.Remote/Transport/AkkaProtocolTransport.cs +++ b/src/core/Akka.Remote/Transport/AkkaProtocolTransport.cs @@ -63,7 +63,6 @@ public class AkkaProtocolException : AkkaException /// The exception that is the cause of the current exception. public AkkaProtocolException(string message, Exception cause = null) : base(message, cause) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -73,7 +72,6 @@ protected AkkaProtocolException(SerializationInfo info, StreamingContext context : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransport.cs b/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransport.cs index f5d7db8754f..49f1bbf497c 100644 --- a/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransport.cs +++ b/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransport.cs @@ -104,7 +104,6 @@ public DotNettyTransportException(string message, Exception cause = null) : base { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -113,7 +112,6 @@ public DotNettyTransportException(string message, Exception cause = null) : base protected DotNettyTransportException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } internal abstract class DotNettyTransport : Transport diff --git a/src/core/Akka.Remote/Transport/FailureInjectorTransportAdapter.cs b/src/core/Akka.Remote/Transport/FailureInjectorTransportAdapter.cs index 5b945814707..61cad66ed6d 100644 --- a/src/core/Akka.Remote/Transport/FailureInjectorTransportAdapter.cs +++ b/src/core/Akka.Remote/Transport/FailureInjectorTransportAdapter.cs @@ -48,7 +48,6 @@ public FailureInjectorException(string msg) Msg = msg; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -58,7 +57,6 @@ private FailureInjectorException(SerializationInfo info, StreamingContext contex : base(info, context) { } -#endif /// /// Retrieves the message of the simulated failure. diff --git a/src/core/Akka.Remote/Transport/Transport.cs b/src/core/Akka.Remote/Transport/Transport.cs index 0c52624adae..7fc11d11fa1 100644 --- a/src/core/Akka.Remote/Transport/Transport.cs +++ b/src/core/Akka.Remote/Transport/Transport.cs @@ -99,7 +99,6 @@ public InvalidAssociationException(string message, Exception cause = null) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -109,7 +108,6 @@ protected InvalidAssociationException(SerializationInfo info, StreamingContext c : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams.TestKit.Tests/Akka.Streams.TestKit.Tests.csproj b/src/core/Akka.Streams.TestKit.Tests/Akka.Streams.TestKit.Tests.csproj index ee0f34ae59b..97bb9020904 100644 --- a/src/core/Akka.Streams.TestKit.Tests/Akka.Streams.TestKit.Tests.csproj +++ b/src/core/Akka.Streams.TestKit.Tests/Akka.Streams.TestKit.Tests.csproj @@ -25,14 +25,6 @@ - - $(DefineConstants);SERIALIZATION;AKKAIO - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Streams.TestKit.Tests/ScriptedTest.cs b/src/core/Akka.Streams.TestKit.Tests/ScriptedTest.cs index e3009182572..903fc39b200 100644 --- a/src/core/Akka.Streams.TestKit.Tests/ScriptedTest.cs +++ b/src/core/Akka.Streams.TestKit.Tests/ScriptedTest.cs @@ -27,10 +27,7 @@ public class ScriptException : Exception public ScriptException() { } public ScriptException(string message) : base(message) { } public ScriptException(string message, Exception inner) : base(message, inner) { } - -#if SERIALIZATION protected ScriptException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } public abstract class ScriptedTest : AkkaSpec diff --git a/src/core/Akka.Streams.TestKit/Akka.Streams.TestKit.csproj b/src/core/Akka.Streams.TestKit/Akka.Streams.TestKit.csproj index b423898c67f..a550bdd7f82 100644 --- a/src/core/Akka.Streams.TestKit/Akka.Streams.TestKit.csproj +++ b/src/core/Akka.Streams.TestKit/Akka.Streams.TestKit.csproj @@ -14,10 +14,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Streams.TestKit/TestSubscriber.cs b/src/core/Akka.Streams.TestKit/TestSubscriber.cs index 39fee165fa4..ef045d46b00 100644 --- a/src/core/Akka.Streams.TestKit/TestSubscriber.cs +++ b/src/core/Akka.Streams.TestKit/TestSubscriber.cs @@ -34,7 +34,7 @@ public OnSubscribe(ISubscription subscription) public override string ToString() => $"TestSubscriber.OnSubscribe({Subscription})"; } - public struct OnNext : ISubscriberEvent + public struct OnNext : ISubscriberEvent, IEquatable> { public readonly T Element; @@ -44,6 +44,21 @@ public OnNext(T element) } public override string ToString() => $"TestSubscriber.OnNext({Element})"; + + public bool Equals(OnNext other) + { + return EqualityComparer.Default.Equals(Element, other.Element); + } + + public override bool Equals(object obj) + { + return obj is OnNext other && Equals(other); + } + + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Element); + } } public sealed class OnComplete: ISubscriberEvent diff --git a/src/core/Akka.Streams.Tests/Akka.Streams.Tests.csproj b/src/core/Akka.Streams.Tests/Akka.Streams.Tests.csproj index aea0d2dbbcb..b28f8f02ec2 100644 --- a/src/core/Akka.Streams.Tests/Akka.Streams.Tests.csproj +++ b/src/core/Akka.Streams.Tests/Akka.Streams.Tests.csproj @@ -35,14 +35,6 @@ - - $(DefineConstants);SERIALIZATION;CONFIGURATION;AKKAIO - - - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Streams.Tests/Dsl/QueueSinkSpec.cs b/src/core/Akka.Streams.Tests/Dsl/QueueSinkSpec.cs index 1d80932b3e3..e9aefadff79 100644 --- a/src/core/Akka.Streams.Tests/Dsl/QueueSinkSpec.cs +++ b/src/core/Akka.Streams.Tests/Dsl/QueueSinkSpec.cs @@ -153,7 +153,7 @@ public void QueueSink_should_timeout_future_when_stream_cannot_provide_data() }, _materializer); } - [Fact(Skip = "Racy, see https://github.com/akkadotnet/akka.net/pull/4424#issuecomment-632284459")] + [Fact] public void QueueSink_should_fail_pull_future_when_stream_is_completed() { this.AssertAllStagesStopped(() => @@ -170,10 +170,8 @@ public void QueueSink_should_fail_pull_future_when_stream_is_completed() var result = queue.PullAsync().Result; result.Should().Be(Option.None); - ((Task)queue.PullAsync()).ContinueWith(t => - { - t.Exception.InnerException.Should().BeOfType(); - }, TaskContinuationOptions.OnlyOnFaulted).Wait(); + var exception = Record.ExceptionAsync(async () => await queue.PullAsync()).Result; + exception.Should().BeOfType(); }, _materializer); } diff --git a/src/core/Akka.Streams.Tests/Dsl/QueueSourceSpec.cs b/src/core/Akka.Streams.Tests/Dsl/QueueSourceSpec.cs index 11a39f98532..9e1d124236b 100644 --- a/src/core/Akka.Streams.Tests/Dsl/QueueSourceSpec.cs +++ b/src/core/Akka.Streams.Tests/Dsl/QueueSourceSpec.cs @@ -261,6 +261,22 @@ public void QueueSource_should_complete_watching_future_with_failure_if_stream_f }, _materializer); } + [Fact] + public void QueueSource_should_complete_watching_future_with_failure_if_materializer_shut_down() + { + this.AssertAllStagesStopped(() => + { + var tempMap = ActorMaterializer.Create(Sys); + var s = this.CreateManualSubscriberProbe(); + var queue = Source.Queue(1, OverflowStrategy.Fail) + .To(Sink.FromSubscriber(s)) + .Run(tempMap); + queue.WatchCompletionAsync().PipeTo(TestActor); + tempMap.Shutdown(); + ExpectMsg(); + }, _materializer); + } + [Fact] public void QueueSource_should_return_false_when_element_was_not_added_to_buffer() { @@ -322,11 +338,12 @@ public void QueueSource_should_fail_offer_future_when_stream_is_completed() .Run(_materializer); var sub = s.ExpectSubscription(); - queue.WatchCompletionAsync().ContinueWith(t => "done").PipeTo(TestActor); + queue.WatchCompletionAsync().ContinueWith(t => Done.Instance).PipeTo(TestActor); sub.Cancel(); - ExpectMsg("done"); + ExpectMsg(Done.Instance); - queue.OfferAsync(1).ContinueWith(t => t.Exception.Should().BeOfType()); + var exception = Record.ExceptionAsync(async () => await queue.OfferAsync(1)).Result; + exception.Should().BeOfType(); }, _materializer); } diff --git a/src/core/Akka.Streams.Tests/Dsl/StageActorRefSpec.cs b/src/core/Akka.Streams.Tests/Dsl/StageActorRefSpec.cs index 04e67b704b1..2cd4f9b68dc 100644 --- a/src/core/Akka.Streams.Tests/Dsl/StageActorRefSpec.cs +++ b/src/core/Akka.Streams.Tests/Dsl/StageActorRefSpec.cs @@ -178,7 +178,7 @@ public async Task A_Graph_stage_ActorRef_must_ignore_and_log_warnings_for_Poison warn.Message.ToString() .Should() .MatchRegex( - " message sent to StageActor\\(akka\\://AkkaSpec/user/StreamSupervisor-[0-9]+/\\$\\$[a-z]+\\) will be ignored, since it is not a real Actor. Use a custom message type to communicate with it instead."); + $" message sent to StageActor\\(akka\\://{Sys.Name}/user/StreamSupervisor-[0-9]+/\\$\\$[a-z]+\\) will be ignored, since it is not a real Actor. Use a custom message type to communicate with it instead."); stageRef.Tell(Kill.Instance); warn = ExpectMsg(TimeSpan.FromSeconds(1)); @@ -186,7 +186,7 @@ public async Task A_Graph_stage_ActorRef_must_ignore_and_log_warnings_for_Poison warn.Message.ToString() .Should() .MatchRegex( - " message sent to StageActor\\(akka\\://AkkaSpec/user/StreamSupervisor-[0-9]+/\\$\\$[a-z]+\\) will be ignored, since it is not a real Actor. Use a custom message type to communicate with it instead."); + $" message sent to StageActor\\(akka\\://{Sys.Name}/user/StreamSupervisor-[0-9]+/\\$\\$[a-z]+\\) will be ignored, since it is not a real Actor. Use a custom message type to communicate with it instead."); source.SetResult(2); diff --git a/src/core/Akka.Streams.Tests/Implementation/StreamLayoutSpec.cs b/src/core/Akka.Streams.Tests/Implementation/StreamLayoutSpec.cs index 7f3f7c5aeec..d8bef953f4b 100644 --- a/src/core/Akka.Streams.Tests/Implementation/StreamLayoutSpec.cs +++ b/src/core/Akka.Streams.Tests/Implementation/StreamLayoutSpec.cs @@ -240,7 +240,6 @@ public void StreamLayout_should_be_able_to_materialize_linear_layouts() CheckMaterialized(runnable); } -#if !CORECLR [Fact(Skip = "We can't catch a StackOverflowException")] public void StreamLayout_should_fail_fusing_when_value_computation_is_too_complex() { @@ -250,7 +249,6 @@ public void StreamLayout_should_fail_fusing_when_value_computation_is_too_comple (flow, i) => flow.MapMaterializedValue(x => x + i)); g.Invoking(flow => Streams.Fusing.Aggressive(flow)).Should().Throw(); } -#endif [Fact] public void StreamLayout_should_not_fail_materialization_when_building_a_large_graph_with_simple_computation_when_starting_from_a_Source() diff --git a/src/core/Akka.Streams/ActorMaterializer.cs b/src/core/Akka.Streams/ActorMaterializer.cs index 5f1666b005b..7392e70d11e 100644 --- a/src/core/Akka.Streams/ActorMaterializer.cs +++ b/src/core/Akka.Streams/ActorMaterializer.cs @@ -242,7 +242,6 @@ public AbruptTerminationException(IActorRef actor) Actor = actor; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -252,7 +251,6 @@ protected AbruptTerminationException(SerializationInfo info, StreamingContext co { Actor = (IActorRef)info.GetValue("Actor", typeof(IActorRef)); } -#endif } /// @@ -267,14 +265,12 @@ public class MaterializationException : Exception /// The exception that is the cause of the current exception. public MaterializationException(string message, Exception innerException) : base(message, innerException) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected MaterializationException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// @@ -289,6 +285,13 @@ public AbruptStageTerminationException(GraphStageLogic logic) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected AbruptStageTerminationException(SerializationInfo info, StreamingContext context) : base(info, context) { } } diff --git a/src/core/Akka.Streams/Akka.Streams.csproj b/src/core/Akka.Streams/Akka.Streams.csproj index ca1b41846f4..e3205c5a705 100644 --- a/src/core/Akka.Streams/Akka.Streams.csproj +++ b/src/core/Akka.Streams/Akka.Streams.csproj @@ -65,9 +65,6 @@ - - $(DefineConstants);CORECLR - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Streams/Dsl/Framing.cs b/src/core/Akka.Streams/Dsl/Framing.cs index db48898e4ff..d64d20c8f73 100644 --- a/src/core/Akka.Streams/Dsl/Framing.cs +++ b/src/core/Akka.Streams/Dsl/Framing.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using Akka.IO; using Akka.Streams.Implementation.Fusing; using Akka.Streams.Implementation.Stages; @@ -122,6 +123,13 @@ public class FramingException : Exception public FramingException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected FramingException(SerializationInfo info, StreamingContext context) : base(info, context) { } } private static readonly Func, int, int> BigEndianDecoder = (enumerator, length) => diff --git a/src/core/Akka.Streams/Dsl/Graph.cs b/src/core/Akka.Streams/Dsl/Graph.cs index aa9bd798d67..cba7566f66a 100644 --- a/src/core/Akka.Streams/Dsl/Graph.cs +++ b/src/core/Akka.Streams/Dsl/Graph.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.Serialization; using Akka.Streams.Implementation; using Akka.Streams.Implementation.Fusing; using Akka.Streams.Implementation.Stages; @@ -1211,6 +1212,13 @@ public PartitionOutOfBoundsException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected PartitionOutOfBoundsException(SerializationInfo info, StreamingContext context) : base(info, context) { } } /// diff --git a/src/core/Akka.Streams/Dsl/Keep.cs b/src/core/Akka.Streams/Dsl/Keep.cs index 5419f23bd2f..09324ad1c51 100644 --- a/src/core/Akka.Streams/Dsl/Keep.cs +++ b/src/core/Akka.Streams/Dsl/Keep.cs @@ -56,11 +56,7 @@ public static class Keep /// TBD public static NotUsed None(TLeft left, TRight right) => NotUsed.Instance; -#if !CORECLR private static readonly RuntimeMethodHandle KeepRightMethodhandle = typeof(Keep).GetMethod(nameof(Right)).MethodHandle; -#else - private static readonly MethodInfo KeepRightMethodInfo = typeof(Keep).GetMethod(nameof(Right)); -#endif /// /// TBD @@ -72,18 +68,10 @@ public static class Keep /// TBD public static bool IsRight(Func fn) { -#if !CORECLR return fn.GetMethodInfo().IsGenericMethod && fn.GetMethodInfo().GetGenericMethodDefinition().MethodHandle.Value == KeepRightMethodhandle.Value; -#else - return fn.GetMethodInfo().IsGenericMethod && fn.GetMethodInfo().GetGenericMethodDefinition().Equals(KeepRightMethodInfo); -#endif } -#if !CORECLR private static readonly RuntimeMethodHandle KeepLeftMethodhandle = typeof(Keep).GetMethod(nameof(Left)).MethodHandle; -#else - private static readonly MethodInfo KeepLeftMethodInfo = typeof(Keep).GetMethod(nameof(Left)); -#endif /// /// TBD @@ -95,11 +83,7 @@ public static bool IsRight(Func fn) /// TBD public static bool IsLeft(Func fn) { -#if !CORECLR return fn.GetMethodInfo().IsGenericMethod && fn.GetMethodInfo().GetGenericMethodDefinition().MethodHandle.Value == KeepLeftMethodhandle.Value; -#else - return fn.GetMethodInfo().IsGenericMethod && fn.GetMethodInfo().GetGenericMethodDefinition().Equals(KeepLeftMethodInfo); -#endif } } } diff --git a/src/core/Akka.Streams/Dsl/One2OneBidiFlow.cs b/src/core/Akka.Streams/Dsl/One2OneBidiFlow.cs index 416d9da8cef..1a4b17b16a0 100644 --- a/src/core/Akka.Streams/Dsl/One2OneBidiFlow.cs +++ b/src/core/Akka.Streams/Dsl/One2OneBidiFlow.cs @@ -6,6 +6,8 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; +using Akka.Pattern; using Akka.Streams.Stage; namespace Akka.Streams.Dsl @@ -41,6 +43,13 @@ public UnexpectedOutputException(object element) : base(element.ToString()) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected UnexpectedOutputException(SerializationInfo info, StreamingContext context) : base(info, context) { } } /// @@ -48,7 +57,14 @@ public UnexpectedOutputException(object element) : base(element.ToString()) /// public class OutputTruncationException : Exception { + public OutputTruncationException() { } + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected OutputTruncationException(SerializationInfo info, StreamingContext context) : base(info, context) { } } /// diff --git a/src/core/Akka.Streams/Dsl/Tcp.cs b/src/core/Akka.Streams/Dsl/Tcp.cs index e62f13c26df..fb47f267297 100644 --- a/src/core/Akka.Streams/Dsl/Tcp.cs +++ b/src/core/Akka.Streams/Dsl/Tcp.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Immutable; using System.Net; +using System.Runtime.Serialization; using System.Threading.Tasks; using Akka.Actor; using Akka.Annotations; @@ -303,6 +304,13 @@ public TcpIdleTimeoutException(string message, TimeSpan duration) : base(message Duration = duration; } + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected TcpIdleTimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public TimeSpan Duration { get; } } } diff --git a/src/core/Akka.Streams/Implementation/ActorPublisher.cs b/src/core/Akka.Streams/Implementation/ActorPublisher.cs index d0894ade36e..72c764b6772 100644 --- a/src/core/Akka.Streams/Implementation/ActorPublisher.cs +++ b/src/core/Akka.Streams/Implementation/ActorPublisher.cs @@ -114,14 +114,12 @@ public class NormalShutdownException : IllegalStateException /// The message that describes the error. public NormalShutdownException(string message) : base(message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected NormalShutdownException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/Implementation/ReactiveStreamsCompliance.cs b/src/core/Akka.Streams/Implementation/ReactiveStreamsCompliance.cs index c3dded30471..cb41aa26b82 100644 --- a/src/core/Akka.Streams/Implementation/ReactiveStreamsCompliance.cs +++ b/src/core/Akka.Streams/Implementation/ReactiveStreamsCompliance.cs @@ -30,14 +30,12 @@ public class SignalThrewException : IllegalStateException, ISpecViolation /// The exception that is the cause of the current exception. public SignalThrewException(string message, Exception cause) : base(message, cause) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected SignalThrewException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/Implementation/ResizableMultiReaderRingBuffer.cs b/src/core/Akka.Streams/Implementation/ResizableMultiReaderRingBuffer.cs index 43f89471f28..b0ab1226c76 100644 --- a/src/core/Akka.Streams/Implementation/ResizableMultiReaderRingBuffer.cs +++ b/src/core/Akka.Streams/Implementation/ResizableMultiReaderRingBuffer.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using Akka.Annotations; using Akka.Streams.Util; @@ -28,7 +29,6 @@ private NothingToReadException() { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -38,7 +38,6 @@ protected NothingToReadException(SerializationInfo info, StreamingContext contex : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/Implementation/Sinks.cs b/src/core/Akka.Streams/Implementation/Sinks.cs index 14356f0f39a..17de6266fb0 100644 --- a/src/core/Akka.Streams/Implementation/Sinks.cs +++ b/src/core/Akka.Streams/Implementation/Sinks.cs @@ -624,10 +624,10 @@ public override void OnUpstreamFailure(Exception e) public override void PostStop() { - if(!_completionSignalled) + if (!_completionSignalled) _promise.TrySetException(new AbruptStageTerminationException(this)); } - + public override void PreStart() => Pull(_stage.In); } @@ -805,12 +805,8 @@ public override void PreStart() Pull(_stage.In); } - public override void PostStop() - { - StopCallback( - promise => - promise.SetException(new IllegalStateException("Stream is terminated. QueueSink is detached"))); - } + public override void PostStop() => + StopCallback(promise => promise.SetException(StreamDetachedException.Instance)); private Action>> Callback() { @@ -1024,7 +1020,8 @@ private void InitInternalSource(Sink sink, TIn firstElement) { var sourceOut = new SubSource(this, firstElement); - try { + try + { var matVal = Source.FromGraph(sourceOut.Source) .RunWith(sink, Interpreter.SubFusingMaterializer); _completion.TrySetResult(matVal); @@ -1034,7 +1031,7 @@ private void InitInternalSource(Sink sink, TIn firstElement) _completion.TrySetException(ex); FailStage(ex); } - + } #region SubSource @@ -1176,7 +1173,7 @@ public void Dispose(bool unregister) } } } - + private sealed class ObservableLogic : GraphStageLogic, IObservable { private readonly ObservableSinkStage _stage; @@ -1216,12 +1213,12 @@ public void Remove(IObserver observer) ImmutableInterlocked.TryRemove(ref _observers, observer, out var _); } - public IDisposable Subscribe(IObserver observer) => + public IDisposable Subscribe(IObserver observer) => ImmutableInterlocked.GetOrAdd(ref _observers, observer, new ObserverDisposable(this, observer)); } #endregion - + public ObservableSinkStage() { diff --git a/src/core/Akka.Streams/Implementation/Sources.cs b/src/core/Akka.Streams/Implementation/Sources.cs index f94b0037b62..b4149d36b9c 100644 --- a/src/core/Akka.Streams/Implementation/Sources.cs +++ b/src/core/Akka.Streams/Implementation/Sources.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Akka.Annotations; using Akka.Pattern; @@ -15,7 +14,6 @@ using Akka.Streams.Implementation.Stages; using Akka.Streams.Stage; using Akka.Streams.Supervision; -using Akka.Streams.Util; using Akka.Util; using Akka.Util.Internal; @@ -170,14 +168,13 @@ public override void PreStart() public override void PostStop() { + var exception = StreamDetachedException.Instance; + _completion.TrySetException(exception); StopCallback(input => { - var offer = input as Offer; - if (offer != null) - { - var promise = offer.CompletionSource; - promise.NonBlockingTrySetException(new IllegalStateException("Stream is terminated. SourceQueue is detached.")); - } + if (!(input is Offer offer)) return; + var promise = offer.CompletionSource; + promise.NonBlockingTrySetException(exception); }); } diff --git a/src/core/Akka.Streams/Implementation/StreamLayout.cs b/src/core/Akka.Streams/Implementation/StreamLayout.cs index fe11dea92b9..35ab676c635 100644 --- a/src/core/Akka.Streams/Implementation/StreamLayout.cs +++ b/src/core/Akka.Streams/Implementation/StreamLayout.cs @@ -2142,7 +2142,6 @@ public MaterializationPanicException(Exception innerException) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -2152,7 +2151,6 @@ protected MaterializationPanicException(SerializationInfo info, StreamingContext : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/Implementation/StreamSubscriptionTimeout.cs b/src/core/Akka.Streams/Implementation/StreamSubscriptionTimeout.cs index 2890edc4468..cd9e6ba3e06 100644 --- a/src/core/Akka.Streams/Implementation/StreamSubscriptionTimeout.cs +++ b/src/core/Akka.Streams/Implementation/StreamSubscriptionTimeout.cs @@ -36,7 +36,6 @@ public SubscriptionTimeoutException(string message, Exception innerException) : { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -45,7 +44,6 @@ public SubscriptionTimeoutException(string message, Exception innerException) : protected SubscriptionTimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/OverflowStrategy.cs b/src/core/Akka.Streams/OverflowStrategy.cs index 5c0994dd28b..123eb357d0a 100644 --- a/src/core/Akka.Streams/OverflowStrategy.cs +++ b/src/core/Akka.Streams/OverflowStrategy.cs @@ -110,7 +110,6 @@ public BufferOverflowException(string message, Exception innerException) : base( { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -119,6 +118,5 @@ public BufferOverflowException(string message, Exception innerException) : base( protected BufferOverflowException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Streams/Shape.cs b/src/core/Akka.Streams/Shape.cs index 1d099693ed8..ec32234adb3 100644 --- a/src/core/Akka.Streams/Shape.cs +++ b/src/core/Akka.Streams/Shape.cs @@ -173,10 +173,7 @@ public Outlet(string name) : base(name) { } /// matters from the outside are the connections that can be made with it, /// otherwise it is just a black box. /// - public abstract class Shape -#if CLONEABLE - : ICloneable -#endif + public abstract class Shape : ICloneable { /// /// Gets list of all input ports. diff --git a/src/core/Akka.Streams/Stage/GraphStage.cs b/src/core/Akka.Streams/Stage/GraphStage.cs index 8534bc09da0..bb33ad80c6e 100644 --- a/src/core/Akka.Streams/Stage/GraphStage.cs +++ b/src/core/Akka.Streams/Stage/GraphStage.cs @@ -2191,14 +2191,12 @@ public class StageActorRefNotInitializedException : Exception public static readonly StageActorRefNotInitializedException Instance = new StageActorRefNotInitializedException(); private StageActorRefNotInitializedException() : base("You must first call GetStageActorRef(StageActorRef.Receive), to initialize the actor's behavior") { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected StageActorRefNotInitializedException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// diff --git a/src/core/Akka.Streams/StageException.cs b/src/core/Akka.Streams/StageException.cs index 5ecbb6265d3..71abcde20ea 100644 --- a/src/core/Akka.Streams/StageException.cs +++ b/src/core/Akka.Streams/StageException.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; namespace Akka.Streams { @@ -22,5 +23,15 @@ public NoSuchElementException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected NoSuchElementException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/core/Akka.Streams/StreamLimitReachedException.cs b/src/core/Akka.Streams/StreamLimitReachedException.cs index b09cfda233b..3fb95a4667d 100644 --- a/src/core/Akka.Streams/StreamLimitReachedException.cs +++ b/src/core/Akka.Streams/StreamLimitReachedException.cs @@ -23,7 +23,6 @@ public StreamLimitReachedException(long max) : base($"Limit of {max} reached") { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -32,6 +31,5 @@ public StreamLimitReachedException(long max) : base($"Limit of {max} reached") protected StreamLimitReachedException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Streams/StreamRefs.cs b/src/core/Akka.Streams/StreamRefs.cs index 8c91644d5eb..2d5247bc38b 100644 --- a/src/core/Akka.Streams/StreamRefs.cs +++ b/src/core/Akka.Streams/StreamRefs.cs @@ -6,12 +6,14 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; using Akka.Actor; using Akka.Pattern; using Akka.Streams.Dsl; using Akka.Streams.Implementation; using Akka.Util; +#pragma warning disable 628 namespace Akka.Streams { /// @@ -58,6 +60,16 @@ public TargetRefNotInitializedYetException() : base( "This should not happen due to proper flow-control, please open a ticket on the issue tracker: https://github.com/akkadotnet/akka.net") { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected TargetRefNotInitializedYetException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } public sealed class StreamRefSubscriptionTimeoutException : IllegalStateException @@ -65,6 +77,16 @@ public sealed class StreamRefSubscriptionTimeoutException : IllegalStateExceptio public StreamRefSubscriptionTimeoutException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected StreamRefSubscriptionTimeoutException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } public sealed class RemoteStreamRefActorTerminatedException : Exception @@ -75,6 +97,16 @@ public sealed class RemoteStreamRefActorTerminatedException : Exception public RemoteStreamRefActorTerminatedException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected RemoteStreamRefActorTerminatedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } public sealed class InvalidSequenceNumberException : IllegalStateException @@ -88,6 +120,16 @@ public InvalidSequenceNumberException(long expectedSeqNr, long gotSeqNr, string ExpectedSeqNr = expectedSeqNr; GotSeqNr = gotSeqNr; } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected InvalidSequenceNumberException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } /// @@ -112,5 +154,15 @@ public InvalidPartnerActorException(IActorRef expectedRef, IActorRef gotRef, str "Multi-cast such as broadcast etc can be implemented by sharing multiple new stream references. ") { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected InvalidPartnerActorException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/core/Akka.Streams/StreamTcpException.cs b/src/core/Akka.Streams/StreamTcpException.cs index 90abae6b157..9ebe2d02564 100644 --- a/src/core/Akka.Streams/StreamTcpException.cs +++ b/src/core/Akka.Streams/StreamTcpException.cs @@ -32,7 +32,6 @@ public StreamTcpException(string message, Exception innerException) : base(messa { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -41,7 +40,22 @@ public StreamTcpException(string message, Exception innerException) : base(messa protected StreamTcpException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif + } + + /// + /// This exception signals that materialized value is already detached from stream. This usually happens + /// when stream is completed and an ActorSystem is shut down while materialized object is still available. + /// + public class StreamDetachedException : Exception + { + /// + /// Initializes a single instance of the class. + /// + public static readonly StreamDetachedException Instance = new StreamDetachedException(); + + private StreamDetachedException() : base("Stream is terminated. Materialized value is detached.") + { + } } /// @@ -58,7 +72,6 @@ private BindFailedException() : base("bind failed") { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -67,7 +80,6 @@ private BindFailedException() : base("bind failed") protected BindFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// @@ -92,7 +104,6 @@ public ConnectionException(string message, Exception innerException) : base(mess { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -101,6 +112,5 @@ public ConnectionException(string message, Exception innerException) : base(mess protected ConnectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/core/Akka.Streams/WatchedActorTerminatedException.cs b/src/core/Akka.Streams/WatchedActorTerminatedException.cs index 1af2a78c1b5..d053febeb72 100644 --- a/src/core/Akka.Streams/WatchedActorTerminatedException.cs +++ b/src/core/Akka.Streams/WatchedActorTerminatedException.cs @@ -24,7 +24,6 @@ public WatchedActorTerminatedException(string stageName, IActorRef actorRef) : base($"Actor watched by [{stageName}] has terminated! Was: {actorRef}") { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -34,6 +33,5 @@ protected WatchedActorTerminatedException(SerializationInfo info, StreamingConte : base(info, context) { } -#endif } } diff --git a/src/core/Akka.TestKit.Tests/Akka.TestKit.Tests.csproj b/src/core/Akka.TestKit.Tests/Akka.TestKit.Tests.csproj index 3bc6331d473..220b17aef65 100644 --- a/src/core/Akka.TestKit.Tests/Akka.TestKit.Tests.csproj +++ b/src/core/Akka.TestKit.Tests/Akka.TestKit.Tests.csproj @@ -20,12 +20,6 @@ - - $(DefineConstants);SERIALIZATION - - - $(DefineConstants);CORECLR - $(DefineConstants);RELEASE diff --git a/src/core/Akka.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs b/src/core/Akka.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs index 47a490146de..13544350e5a 100644 --- a/src/core/Akka.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs +++ b/src/core/Akka.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs @@ -6,125 +6,170 @@ //----------------------------------------------------------------------- using System; +using Akka.Actor; using Akka.Event; using Akka.TestKit; +using Akka.TestKit.Tests.Xunit2.TestEventListenerTests; using Xunit; using Xunit.Sdk; namespace Akka.Testkit.Tests.TestEventListenerTests { - //public class ExceptionEventFilterTests : EventFilterTestBase - //{ - // public ExceptionEventFilterTests() - // : base("akka.logLevel=ERROR") - // { - // } - // public class TestFinished : Exception { } - // public class SomeException : Exception { } - - // protected override void SendInitLoggerMessage(InitLoggerMessage message) - // { - // throw new NotImplementedException(); - // } - - // protected override void AfterTest() - // { - // //After every test we make sure no uncatched messages have been logged - // EnsureNoMoreLoggedMessages(); - // base.AfterTest(); - // } - - // private void EnsureNoMoreLoggedMessages() - // { - // //We log a TestFinished exception. When it arrives to TestActor we know no other message has been logged7 - // //If we receive something else it means another message was logged, and ExpectMsg will fail - // Log.Error(new TestFinished(), "Finished"); - // ExpectMsg(err => err.Cause is TestFinished,"cause to be "); - // } - - // [Fact] - // public void SingleExceptionIsIntercepted() - // { - // EventFilter.Exception().Intercept(() => Log.Error(new SomeException(), "whatever")); - // } - - // [Fact] - // public void CanInterceptMessagesWhenStartIsSpecified() - // { - // EventFilter.Exception(start: "what").Intercept(() => Log.Error(new SomeException(), "whatever")); - // } - - // [Fact] - // public void DoNotInterceptMessagesWhenStartDoesNotMatch() - // { - // EventFilter.Exception(start: "this is clearly not in message").Intercept(() => Log.Error(new SomeException(), "whatever")); - // ExpectMsg(err => (string)err.Message == "whatever"); - // } - - // [Fact] - // public void CanInterceptMessagesWhenMessageIsSpecified() - // { - // EventFilter.Exception(message: "whatever").Intercept(() => Log.Error(new SomeException(), "whatever")); - // } - - // [Fact] - // public void DoNotInterceptMessagesWhenMessageDoesNotMatch() - // { - // EventFilter.Exception(message: "this is clearly not the message").Intercept(() => Log.Error(new SomeException(), "whatever")); - // ExpectMsg(err => (string)err.Message == "whatever"); - // } - - // [Fact] - // public void CanInterceptMessagesWhenContainsIsSpecified() - // { - // EventFilter.Exception(contains: "ate").Intercept(() => Log.Error(new SomeException(), "whatever")); - // } - - // [Fact] - // public void DoNotInterceptMessagesWhenContainsDoesNotMatch() - // { - // EventFilter.Exception(contains: "this is clearly not in the message").Intercept(() => Log.Error(new SomeException(), "whatever")); - // ExpectMsg(err => (string)err.Message == "whatever"); - // } - - - // [Fact] - // public void CanInterceptMessagesWhenSourceIsSpecified() - // { - // EventFilter.Exception(source: GetType().FullName).Intercept(() => Log.Error(new SomeException(), "whatever")); - // } - - // [Fact] - // public void DoNotInterceptMessagesWhenSourceDoesNotMatch() - // { - // EventFilter.Exception(source: "this is clearly not the source").Intercept(() => Log.Error(new SomeException(), "whatever")); - // ExpectMsg(err => (string)err.Message == "whatever"); - // } - - - // [Fact] - // public void SpecifiedNumbersOfExceptionsCanBeIntercepted() - // { - // EventFilter.Exception(occurrences: 2).Intercept(() => - // { - // Log.Error(new SomeException(), "whatever"); - // Log.Error(new SomeException(), "whatever"); - // }); - // } - - // [Fact] - // public void ShouldFailIfMoreExceptionsThenSpecifiedAreLogged() - // { - // var exception = XAssert.Throws(() => - // EventFilter.Exception(occurrences: 2).Intercept(() => - // { - // Log.Error(new SomeException(), "whatever"); - // Log.Error(new SomeException(), "whatever"); - // Log.Error(new SomeException(), "whatever"); - // })); - // Assert.Contains("1 messages too many", exception.Message, StringComparison.OrdinalIgnoreCase); - // } - - //} + public class ExceptionEventFilterTests : EventFilterTestBase + { + public ExceptionEventFilterTests() + : base("akka.logLevel=ERROR") + { + } + public class SomeException : Exception { } + + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, nameof(ExceptionEventFilterTests), GetType(), message)); + } + + [Fact] + public void SingleExceptionIsIntercepted() + { + EventFilter.Exception() + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void CanInterceptMessagesWhenStartIsSpecified() + { + EventFilter.Exception(start: "what") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void DoNotInterceptMessagesWhenStartDoesNotMatch() + { + EventFilter.Exception(start: "this is clearly not in message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } + + [Fact] + public void CanInterceptMessagesWhenMessageIsSpecified() + { + EventFilter.Exception(message: "whatever") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void DoNotInterceptMessagesWhenMessageDoesNotMatch() + { + EventFilter.Exception(message: "this is clearly not the message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } + + [Fact] + public void CanInterceptMessagesWhenContainsIsSpecified() + { + EventFilter.Exception(contains: "ate") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void DoNotInterceptMessagesWhenContainsDoesNotMatch() + { + EventFilter.Exception(contains: "this is clearly not in the message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } + + + [Fact] + public void CanInterceptMessagesWhenSourceIsSpecified() + { + EventFilter.Exception(source: LogSource.Create(this, Sys).Source) + .ExpectOne(() => + { + Log.Error(new SomeException(), "whatever"); + }); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void DoNotInterceptMessagesWhenSourceDoesNotMatch() + { + EventFilter.Exception(source: "this is clearly not the source"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } + + + [Fact] + public void SpecifiedNumbersOfExceptionsCanBeIntercepted() + { + EventFilter.Exception() + .Expect(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + }); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public void ShouldFailIfMoreExceptionsThenSpecifiedAreLogged() + { + var exception = XAssert.Throws(() => + EventFilter.Exception().Expect(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + })); + Assert.Contains("1 message too many", exception.Message, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void ShouldReportCorrectMessageCount() + { + var toSend = "Eric Cartman"; + var actor = ActorOf( ExceptionTestActor.Props() ); + + EventFilter + .Exception(source: actor.Path.ToString()) + // expecting 2 because the same exception is logged in PostRestart + .Expect(2, () => actor.Tell( toSend )); + } + + internal sealed class ExceptionTestActor : UntypedActor + { + private ILoggingAdapter Log { get; } = Context.GetLogger(); + + protected override void PostRestart(Exception reason) + { + Log.Error(reason, "[PostRestart]"); + base.PostRestart(reason); + } + + protected override void OnReceive( object message ) + { + switch (message) + { + case string msg: + throw new InvalidOperationException( "I'm sailing away. Set an open course" ); + + default: + Unhandled( message ); + break; + } + } + + public static Props Props() + { + return Actor.Props.Create( () => new ExceptionTestActor() ); + } + } + } } diff --git a/src/core/Akka.TestKit/Akka.TestKit.csproj b/src/core/Akka.TestKit/Akka.TestKit.csproj index ff46c35b560..4c97416d237 100644 --- a/src/core/Akka.TestKit/Akka.TestKit.csproj +++ b/src/core/Akka.TestKit/Akka.TestKit.csproj @@ -26,10 +26,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.TestKit/Internal/InternalTestActor.cs b/src/core/Akka.TestKit/Internal/InternalTestActor.cs index bc7c6c6b94b..04cdd27af90 100644 --- a/src/core/Akka.TestKit/Internal/InternalTestActor.cs +++ b/src/core/Akka.TestKit/Internal/InternalTestActor.cs @@ -5,6 +5,7 @@ // //----------------------------------------------------------------------- +using System; using System.Collections.Concurrent; using Akka.Actor; using Akka.Event; @@ -38,7 +39,19 @@ public InternalTestActor(ITestActorQueue queue) /// TBD protected override bool Receive(object message) { - global::System.Diagnostics.Debug.WriteLine("TestActor received " + message); + try + { + global::System.Diagnostics.Debug.WriteLine("TestActor received " + message); + } + catch (FormatException) + { + if (message is LogEvent evt && evt.Message is LogMessage msg) + global::System.Diagnostics.Debug.WriteLine( + $"TestActor received a malformed formatted message. Template:[{msg.Format}], args:[{string.Join(",", msg.Args)}]"); + else + throw; + } + var setIgnore = message as TestKit.TestActor.SetIgnore; if(setIgnore != null) { diff --git a/src/core/Akka.Tests.Shared.Internals/Akka.Tests.Shared.Internals.csproj b/src/core/Akka.Tests.Shared.Internals/Akka.Tests.Shared.Internals.csproj index 465234b72e3..60651228b04 100644 --- a/src/core/Akka.Tests.Shared.Internals/Akka.Tests.Shared.Internals.csproj +++ b/src/core/Akka.Tests.Shared.Internals/Akka.Tests.Shared.Internals.csproj @@ -17,10 +17,6 @@ - - $(DefineConstants);CORECLR - - $(DefineConstants);RELEASE diff --git a/src/core/Akka.Tests.Shared.Internals/AkkaSpec.cs b/src/core/Akka.Tests.Shared.Internals/AkkaSpec.cs index 078daeaef9a..9b6a2c94e7f 100644 --- a/src/core/Akka.Tests.Shared.Internals/AkkaSpec.cs +++ b/src/core/Akka.Tests.Shared.Internals/AkkaSpec.cs @@ -100,10 +100,6 @@ protected virtual void AfterTermination() { } private static string GetCallerName() { -#if CORECLR - // TODO: CORECLR FIX IT - var name = "AkkaSpec"; -#else var systemNumber = Interlocked.Increment(ref _systemNumber); var stackTrace = new StackTrace(0); var name = stackTrace.GetFrames(). @@ -112,7 +108,7 @@ private static string GetCallerName() SkipWhile(m => m.DeclaringType.Name == "AkkaSpec"). Select(m => _nameReplaceRegex.Replace(m.DeclaringType.Name + "-" + systemNumber, "-")). FirstOrDefault() ?? "test"; -#endif + return name; } diff --git a/src/core/Akka.Tests/Actor/Dispatch/Bug2640Spec.cs b/src/core/Akka.Tests/Actor/Dispatch/Bug2640Spec.cs index 2b2b5806483..73a4980df0d 100644 --- a/src/core/Akka.Tests/Actor/Dispatch/Bug2640Spec.cs +++ b/src/core/Akka.Tests/Actor/Dispatch/Bug2640Spec.cs @@ -46,6 +46,12 @@ public class ThreadReporterActor : ReceiveActor { public ThreadReporterActor() { + // Improve reliability of ForkJoinExecutorShouldShutdownUponAllActorsTerminating + // test which intermittently can fail because all messages are actually serviced + // by a single thread from the pool. Happens under load, presumably when the processor + // finds it more efficient to keep thread locality of the execution. + Thread.Sleep(1); + Receive(_ => Sender.Tell(Thread.CurrentThread)); } } @@ -90,11 +96,10 @@ public void ForkJoinExecutorShouldShutdownUponAllActorsTerminating() Dictionary threads = null; Watch(actor); - var msgCount = 100; - for (var i = 0; i < msgCount; i++) + for (var i = 0; i < 100; i++) actor.Tell(GetThread.Instance); - threads = ReceiveN(msgCount).Cast().GroupBy(x => x.ManagedThreadId) + threads = ReceiveN(100).Cast().GroupBy(x => x.ManagedThreadId) .ToDictionary(x => x.Key, grouping => grouping.First()); Sys.Stop(actor); diff --git a/src/core/Akka.Tests/Akka.Tests.csproj b/src/core/Akka.Tests/Akka.Tests.csproj index 279e581df92..1c9d08c164f 100644 --- a/src/core/Akka.Tests/Akka.Tests.csproj +++ b/src/core/Akka.Tests/Akka.Tests.csproj @@ -21,21 +21,11 @@ - - - - - - - + - - $(DefineConstants);SERIALIZATION;CONFIGURATION - - $(DefineConstants);CORECLR diff --git a/src/core/Akka.Tests/IO/TcpIntegrationSpec.cs b/src/core/Akka.Tests/IO/TcpIntegrationSpec.cs index cb8abc1c4d3..0b56cb5c43a 100644 --- a/src/core/Akka.Tests/IO/TcpIntegrationSpec.cs +++ b/src/core/Akka.Tests/IO/TcpIntegrationSpec.cs @@ -21,9 +21,7 @@ using Xunit; using Xunit.Abstractions; using FluentAssertions; -#if CORECLR using System.Runtime.InteropServices; -#endif namespace Akka.Tests.IO { @@ -174,14 +172,10 @@ public void The_TCP_transport_implementation_should_properly_handle_connection_a public void The_TCP_transport_implementation_should_properly_support_connecting_to_DNS_endpoints(AddressFamily family) { // Aaronontheweb, 9/2/2017 - POSIX-based OSES are still having trouble with IPV6 DNS resolution -#if CORECLR - if(!System.Runtime.InteropServices.RuntimeInformation + if(!RuntimeInformation .IsOSPlatform(OSPlatform.Windows) && family == AddressFamily.InterNetworkV6) - return; -#else - if (RuntimeDetector.IsMono && family == AddressFamily.InterNetworkV6) // same as above return; -#endif + var serverHandler = CreateTestProbe(); var bindCommander = CreateTestProbe(); bindCommander.Send(Sys.Tcp(), new Tcp.Bind(serverHandler.Ref, new IPEndPoint(family == AddressFamily.InterNetwork ? IPAddress.Loopback diff --git a/src/core/Akka.Tests/Loggers/LoggerSpec.cs b/src/core/Akka.Tests/Loggers/LoggerSpec.cs new file mode 100644 index 00000000000..17ba5c31a1b --- /dev/null +++ b/src/core/Akka.Tests/Loggers/LoggerSpec.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Akka.Actor; +using Akka.Configuration; +using Akka.Event; +using Akka.TestKit; +using Xunit; +using Xunit.Abstractions; +using FluentAssertions; + +namespace Akka.Tests.Loggers +{ + public class LoggerSpec : AkkaSpec + { + private static readonly Config Config = ConfigurationFactory.ParseString(@" +akka.loglevel = DEBUG +akka.stdout-loglevel = DEBUG"); + + public static readonly (string t, string[] p) Case = + ("This is {0} a {1} janky formatting. {4}", new []{"also", "very", "not cool"}); + + public LoggerSpec(ITestOutputHelper output) : base(Config, output) + { } + + [Fact] + public void TestOutputLogger_WithBadFormattingMustNotThrow() + { + // Need to wait until TestOutputLogger initializes + Thread.Sleep(200); + Sys.EventStream.Subscribe(TestActor, typeof(LogEvent)); + + Sys.Log.Error(new FakeException("BOOM"), Case.t, Case.p); + ExpectMsg().Cause.Should().BeOfType(); + ExpectMsg().Cause.Should().BeOfType(); + + Sys.Log.Warning(Case.t, Case.p); + ExpectMsg(); + ExpectMsg().Cause.Should().BeOfType(); + + Sys.Log.Info(Case.t, Case.p); + ExpectMsg(); + ExpectMsg().Cause.Should().BeOfType(); + + Sys.Log.Debug(Case.t, Case.p); + ExpectMsg(); + ExpectMsg().Cause.Should().BeOfType(); + } + + [Fact] + public void DefaultLogger_WithBadFormattingMustNotThrow() + { + var config = ConfigurationFactory.ParseString("akka.loggers = [\"Akka.Event.DefaultLogger\"]"); + var sys2 = ActorSystem.Create("DefaultLoggerTest", config.WithFallback(Sys.Settings.Config)); + var probe = CreateTestProbe(sys2); + + sys2.EventStream.Subscribe(probe, typeof(LogEvent)); + + sys2.Log.Error(new FakeException("BOOM"), Case.t, Case.p); + probe.ExpectMsg().Cause.Should().BeOfType(); + + sys2.Log.Warning(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Log.Info(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Log.Debug(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Terminate().Wait(); + } + + [Fact] + public void StandardOutLogger_WithBadFormattingMustNotThrow() + { + var config = ConfigurationFactory.ParseString("akka.loggers = [\"Akka.Event.StandardOutLogger\"]"); + var sys2 = ActorSystem.Create("StandardOutLoggerTest", config.WithFallback(Sys.Settings.Config)); + var probe = CreateTestProbe(sys2); + + sys2.EventStream.Subscribe(probe, typeof(LogEvent)); + + sys2.Log.Error(new FakeException("BOOM"), Case.t, Case.p); + probe.ExpectMsg().Cause.Should().BeOfType(); + + sys2.Log.Warning(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Log.Info(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Log.Debug(Case.t, Case.p); + probe.ExpectMsg(); + + sys2.Terminate().Wait(); + } + + [Theory] + [MemberData(nameof(LogEventFactory))] + public void StandardOutLogger_PrintLogEvent_WithBadLogFormattingMustNotThrow(LogEvent @event) + { + var obj = new object(); + obj.Invoking(o => StandardOutLogger.PrintLogEvent(@event)).Should().NotThrow(); + } + + public static IEnumerable LogEventFactory() + { + var ex = new FakeException("BOOM"); + var logSource = LogSource.Create(nameof(LoggerSpec)); + var ls = logSource.Source; + var lc = logSource.Type; + var formatter = new DefaultLogMessageFormatter(); + + yield return new object[] { new Error(ex, ls, lc, new LogMessage(formatter, Case.t, Case.p)) }; + + yield return new object[] {new Warning(ex, ls, lc, new LogMessage(formatter, Case.t, Case.p))}; + + yield return new object[] {new Info(ex, ls, lc, new LogMessage(formatter, Case.t, Case.p))}; + + yield return new object[] {new Debug(ex, ls, lc, new LogMessage(formatter, Case.t, Case.p))}; + } + + private class FakeException : Exception + { + public FakeException(string message) : base(message) + { } + } + } +} diff --git a/src/core/Akka.Tests/Pattern/CircuitBreakerSpec.cs b/src/core/Akka.Tests/Pattern/CircuitBreakerSpec.cs index 99d7837345d..9dd73f13cc9 100644 --- a/src/core/Akka.Tests/Pattern/CircuitBreakerSpec.cs +++ b/src/core/Akka.Tests/Pattern/CircuitBreakerSpec.cs @@ -515,12 +515,10 @@ public TestException( string message, Exception innerException ) { } -#if SERIALIZATION protected TestException( SerializationInfo info, StreamingContext context ) : base( info, context ) { } -#endif } } diff --git a/src/core/Akka/Actor/Address.cs b/src/core/Akka/Actor/Address.cs index 08f742fa28c..9e13a9374d2 100644 --- a/src/core/Akka/Actor/Address.cs +++ b/src/core/Akka/Actor/Address.cs @@ -23,10 +23,7 @@ namespace Akka.Actor /// for example a remote transport would want to associate additional /// information with an address, then this must be done externally. /// - public sealed class Address : IEquatable
, IComparable
, IComparable, ISurrogated -#if CLONEABLE - , ICloneable -#endif + public sealed class Address : IEquatable
, IComparable
, IComparable, ISurrogated, ICloneable { #region comparer diff --git a/src/core/Akka/Actor/Exceptions.cs b/src/core/Akka/Actor/Exceptions.cs index 9a104e247d1..be2080319f1 100644 --- a/src/core/Akka/Actor/Exceptions.cs +++ b/src/core/Akka/Actor/Exceptions.cs @@ -32,7 +32,6 @@ protected AkkaException(string message, Exception cause = null) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -42,7 +41,6 @@ protected AkkaException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif /// /// The exception that is the cause of the current exception. @@ -74,7 +72,6 @@ public InvalidActorNameException(string message, Exception innerException) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -84,7 +81,6 @@ protected InvalidActorNameException(SerializationInfo info, StreamingContext con : base(info, context) { } -#endif } /// @@ -101,7 +97,6 @@ public AskTimeoutException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -111,7 +106,6 @@ protected AskTimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// @@ -127,6 +121,16 @@ public class ActorInterruptedException : AkkaException /// TBD /// TBD public ActorInterruptedException(string message = null, Exception cause = null) : base(message, cause) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected ActorInterruptedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } /// @@ -173,7 +177,6 @@ public ActorInitializationException(IActorRef actor, string message, Exception c Actor = actor; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -191,7 +194,6 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont info.AddValue("Actor", Actor); base.GetObjectData(info, context); } -#endif /// /// Retrieves the actor whose initialization logic failed. @@ -243,7 +245,6 @@ public LoggerInitializationException(string message, Exception cause = null) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -253,7 +254,6 @@ protected LoggerInitializationException(SerializationInfo info, StreamingContext : base(info, context) { } -#endif } /// @@ -275,7 +275,6 @@ public ActorKilledException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -285,7 +284,6 @@ protected ActorKilledException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } /// @@ -303,7 +301,6 @@ public IllegalActorStateException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -313,7 +310,6 @@ protected IllegalActorStateException(SerializationInfo info, StreamingContext co : base(info, context) { } -#endif } /// @@ -330,7 +326,6 @@ public IllegalActorNameException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -340,7 +335,6 @@ protected IllegalActorNameException(SerializationInfo info, StreamingContext con : base(info, context) { } -#endif } /// @@ -349,8 +343,6 @@ protected IllegalActorNameException(SerializationInfo info, StreamingContext con /// public class DeathPactException : AkkaException { - private readonly IActorRef _deadActor; - /// /// Initializes a new instance of the class. /// @@ -358,10 +350,9 @@ public class DeathPactException : AkkaException public DeathPactException(IActorRef deadActor) : base("Monitored actor [" + deadActor + "] terminated") { - _deadActor = deadActor; + DeadActor = deadActor; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -370,16 +361,20 @@ public DeathPactException(IActorRef deadActor) protected DeathPactException(SerializationInfo info, StreamingContext context) : base(info, context) { + DeadActor = (IActorRef)info.GetValue("DeadActor", typeof(IActorRef)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) throw new ArgumentNullException(nameof(info)); + info.AddValue("DeadActor", DeadActor); + base.GetObjectData(info, context); } -#endif /// /// Retrieves the actor that has been terminated. /// - public IActorRef DeadActor - { - get { return _deadActor; } - } + public IActorRef DeadActor { get; } } /// @@ -390,12 +385,10 @@ public IActorRef DeadActor /// hence it is only visible as log entry on the event stream. /// /// - public class PreRestartException : AkkaException + public class PreRestartException : ActorInitializationException { - private IActorRef Actor; - private Exception e; //TODO: what is this? - private Exception exception; - private object optionalMessage; + public Exception RestartException { get; } //TODO: what is this? + public object OptionalMessage { get; } /// /// Initializes a new instance of the class. @@ -405,14 +398,13 @@ public class PreRestartException : AkkaException /// The exception which caused the restart in the first place. /// The message which was optionally passed into . public PreRestartException(IActorRef actor, Exception restartException, Exception cause, object optionalMessage) + :base(actor,"Exception pre restart (" + (restartException == null ?"null" : restartException.GetType().ToString()) + ")", cause) { Actor = actor; - e = restartException; - exception = cause; - this.optionalMessage = optionalMessage; + RestartException = restartException; + OptionalMessage = optionalMessage; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -421,8 +413,18 @@ public PreRestartException(IActorRef actor, Exception restartException, Exceptio protected PreRestartException(SerializationInfo info, StreamingContext context) : base(info, context) { + RestartException = (Exception)info.GetValue("RestartException", typeof(Exception)); + OptionalMessage = info.GetValue("OptionalMessage", typeof(object)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) throw new ArgumentNullException(nameof(info)); + info.AddValue("RestartException", RestartException); + info.AddValue("OptionalMessage", OptionalMessage); + base.GetObjectData(info, context); } -#endif + } /// @@ -431,8 +433,6 @@ protected PreRestartException(SerializationInfo info, StreamingContext context) /// public class PostRestartException : ActorInitializationException { - private readonly Exception _originalCause; - /// /// Initializes a new instance of the class. /// @@ -442,10 +442,9 @@ public class PostRestartException : ActorInitializationException public PostRestartException(IActorRef actor, Exception cause, Exception originalCause) :base(actor,"Exception post restart (" + (originalCause == null ?"null" : originalCause.GetType().ToString()) + ")", cause) { - _originalCause = originalCause; + OriginalCause = originalCause; } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -454,13 +453,20 @@ public PostRestartException(IActorRef actor, Exception cause, Exception original protected PostRestartException(SerializationInfo info, StreamingContext context) : base(info, context) { + OriginalCause = (Exception)info.GetValue("OriginalCause", typeof(Exception)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) throw new ArgumentNullException(nameof(info)); + info.AddValue("OriginalCause", OriginalCause); + base.GetObjectData(info, context); } -#endif /// /// Retrieves the exception which caused the restart in the first place. /// - public Exception OriginalCause { get { return _originalCause; } } + public Exception OriginalCause { get; } } /// @@ -476,7 +482,6 @@ public ActorNotFoundException() { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -486,7 +491,6 @@ protected ActorNotFoundException(SerializationInfo info, StreamingContext contex : base(info, context) { } -#endif /// /// that takes a descriptive and optional . @@ -522,7 +526,6 @@ public InvalidMessageException(string message) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -532,7 +535,6 @@ protected InvalidMessageException(SerializationInfo info, StreamingContext conte : base(info, context) { } -#endif } } diff --git a/src/core/Akka/Actor/Props.cs b/src/core/Akka/Actor/Props.cs index 61babb75c13..d01ddbe2b07 100644 --- a/src/core/Akka/Actor/Props.cs +++ b/src/core/Akka/Actor/Props.cs @@ -768,12 +768,7 @@ private DynamicProps(Props copy, Func invoker) protected override Props Copy() { var initialCopy = base.Copy(); -#if CLONEABLE var invokerCopy = (Func)invoker.Clone(); -#else - // TODO: CORECLR FIX IT - var invokerCopy = invoker; -#endif return new DynamicProps(initialCopy, invokerCopy); } diff --git a/src/core/Akka/Actor/Scheduler/SchedulerException.cs b/src/core/Akka/Actor/Scheduler/SchedulerException.cs index 7c110e01b12..a107bc0b936 100644 --- a/src/core/Akka/Actor/Scheduler/SchedulerException.cs +++ b/src/core/Akka/Actor/Scheduler/SchedulerException.cs @@ -5,6 +5,8 @@ // //----------------------------------------------------------------------- +using System.Runtime.Serialization; + namespace Akka.Actor { /// @@ -18,6 +20,16 @@ public sealed class SchedulerException : AkkaException /// /// The message that describes the error. public SchedulerException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected SchedulerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/core/Akka/Actor/Stash/StashOverflowException.cs b/src/core/Akka/Actor/Stash/StashOverflowException.cs index 87e8f6a4c8a..fbefa038dd1 100644 --- a/src/core/Akka/Actor/Stash/StashOverflowException.cs +++ b/src/core/Akka/Actor/Stash/StashOverflowException.cs @@ -22,7 +22,6 @@ public class StashOverflowException : AkkaException /// The exception that is the cause of the current exception. public StashOverflowException(string message, Exception cause = null) : base(message, cause) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -32,6 +31,5 @@ protected StashOverflowException(SerializationInfo info, StreamingContext contex : base(info, context) { } -#endif } } diff --git a/src/core/Akka/Akka.csproj b/src/core/Akka/Akka.csproj index 28b2abd163d..2f0d47d7804 100644 --- a/src/core/Akka/Akka.csproj +++ b/src/core/Akka/Akka.csproj @@ -16,6 +16,8 @@ + + @@ -37,10 +39,6 @@ - - $(DefineConstants);CORECLR;CONFIGURATION; - - $(DefineConstants);RELEASE diff --git a/src/core/Akka/Configuration/ConfigurationException.cs b/src/core/Akka/Configuration/ConfigurationException.cs index 2e123d2ab7e..30c20646a5a 100644 --- a/src/core/Akka/Configuration/ConfigurationException.cs +++ b/src/core/Akka/Configuration/ConfigurationException.cs @@ -40,7 +40,6 @@ public ConfigurationException(string message, Exception exception): base(message { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -50,7 +49,6 @@ protected ConfigurationException(SerializationInfo info, StreamingContext contex : base(info, context) { } -#endif } } diff --git a/src/core/Akka/Configuration/ConfigurationFactory.cs b/src/core/Akka/Configuration/ConfigurationFactory.cs index 220963ec7aa..ac2b9aec733 100644 --- a/src/core/Akka/Configuration/ConfigurationFactory.cs +++ b/src/core/Akka/Configuration/ConfigurationFactory.cs @@ -61,12 +61,8 @@ public static Config ParseString(string hocon) /// The configuration defined in the configuration file. public static Config Load() { -#if CONFIGURATION var section = (AkkaConfigurationSection)System.Configuration.ConfigurationManager.GetSection("akka") ?? new AkkaConfigurationSection(); return section.AkkaConfig; -#else - return ConfigurationFactory.Empty; -#endif } /// diff --git a/src/core/Akka/Configuration/Hocon/AkkaConfigurationSection.cs b/src/core/Akka/Configuration/Hocon/AkkaConfigurationSection.cs index 687d8bb2b31..2b9f2dba518 100644 --- a/src/core/Akka/Configuration/Hocon/AkkaConfigurationSection.cs +++ b/src/core/Akka/Configuration/Hocon/AkkaConfigurationSection.cs @@ -5,7 +5,6 @@ // //----------------------------------------------------------------------- -#if CONFIGURATION using System.Configuration; namespace Akka.Configuration.Hocon @@ -67,4 +66,3 @@ public HoconConfigurationElement Hocon } } } -#endif diff --git a/src/core/Akka/Configuration/Hocon/CDataConfigurationElement.cs b/src/core/Akka/Configuration/Hocon/CDataConfigurationElement.cs index deb49e041ef..ea9a839d498 100644 --- a/src/core/Akka/Configuration/Hocon/CDataConfigurationElement.cs +++ b/src/core/Akka/Configuration/Hocon/CDataConfigurationElement.cs @@ -5,7 +5,6 @@ // //----------------------------------------------------------------------- -#if CONFIGURATION using System.Configuration; using System.Xml; @@ -64,4 +63,3 @@ protected override void DeserializeElement(XmlReader reader, bool serializeColle } } } -#endif diff --git a/src/core/Akka/Configuration/Hocon/HoconConfigurationElement.cs b/src/core/Akka/Configuration/Hocon/HoconConfigurationElement.cs index c8ab219be96..177863060a4 100644 --- a/src/core/Akka/Configuration/Hocon/HoconConfigurationElement.cs +++ b/src/core/Akka/Configuration/Hocon/HoconConfigurationElement.cs @@ -5,7 +5,6 @@ // //----------------------------------------------------------------------- -#if CONFIGURATION using System.Configuration; namespace Akka.Configuration.Hocon @@ -42,4 +41,3 @@ public string Content } } } -#endif diff --git a/src/core/Akka/Dispatch/ExecutorService.cs b/src/core/Akka/Dispatch/ExecutorService.cs index adc68395b89..6864e970b86 100644 --- a/src/core/Akka/Dispatch/ExecutorService.cs +++ b/src/core/Akka/Dispatch/ExecutorService.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Runtime.Serialization; using Akka.Actor; using Akka.Annotations; @@ -70,6 +71,13 @@ public class RejectedExecutionException : AkkaException /// TBD /// TBD public RejectedExecutionException(string message = null, Exception inner = null) : base(message, inner) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected RejectedExecutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } diff --git a/src/core/Akka/Event/EventBusUnsubscriber.cs b/src/core/Akka/Event/EventBusUnsubscriber.cs index ba332dd9273..3bd9b3fd7e5 100644 --- a/src/core/Akka/Event/EventBusUnsubscriber.cs +++ b/src/core/Akka/Event/EventBusUnsubscriber.cs @@ -8,6 +8,7 @@ using Akka.Actor; using Akka.Actor.Internal; using Akka.Annotations; +using Akka.Dispatch; using Akka.Util.Internal; namespace Akka.Event @@ -176,7 +177,7 @@ public static EventStreamUnsubscribersProvider Instance /// TBD public void Start(ActorSystemImpl system, EventStream eventStream, bool debug) { - system.SystemActorOf(Props.Create(eventStream, system, debug), + system.SystemActorOf(Props.Create(eventStream, system, debug).WithDispatcher(Dispatchers.InternalDispatcherId), string.Format("EventStreamUnsubscriber-{0}", _unsubscribersCounter.IncrementAndGet())); } } diff --git a/src/core/Akka/Event/StandardOutLogger.cs b/src/core/Akka/Event/StandardOutLogger.cs index e99280d120e..6493c136fc4 100644 --- a/src/core/Akka/Event/StandardOutLogger.cs +++ b/src/core/Akka/Event/StandardOutLogger.cs @@ -135,24 +135,26 @@ public static void PrintLogEvent(LogEvent logEvent) StandardOutWriter.WriteLine(logEvent.ToString(), color); } - catch (FormatException) + catch (FormatException ex) { /* - * If we've reached this point, the `logEvent` itself is informatted incorrectly. + * If we've reached this point, the `logEvent` itself is formatted incorrectly. * Therefore we have to treat the data inside the `logEvent` as suspicious and avoid throwing * a second FormatException. */ var sb = new StringBuilder(); - sb.AppendFormat("[ERROR][{0}][Thread {1}][StandardOutLogger] ", logEvent.Timestamp, 0); - sb.AppendFormat("Encoutered System.FormatException while recording log: [" + - logEvent.LogLevel().PrettyNameFor() + "]") - .AppendFormat("[" + logEvent.LogSource + "][" + logEvent.Message + "]"); + sb.AppendFormat("[ERROR][{0}]", logEvent.Timestamp) + .AppendFormat("[Thread {0}]", logEvent.Thread.ManagedThreadId.ToString().PadLeft(4, '0')) + .AppendFormat("[{0}] ", nameof(StandardOutLogger)) + .AppendFormat("Encountered System.FormatException while recording log: [{0}]", logEvent.LogLevel().PrettyNameFor()) + .AppendFormat("[{0}]. ", logEvent.LogSource) + .Append(ex.Message); string msg; switch (logEvent.Message) { case LogMessage formatted: // a parameterized log - msg = "str=[" + formatted.Format + "],args=["+ string.Join(",", formatted.Args) +"]"; + msg = " str=[" + formatted.Format + "], args=["+ string.Join(",", formatted.Args) +"]"; break; case string unformatted: // pre-formatted or non-parameterized log msg = unformatted; @@ -163,7 +165,7 @@ public static void PrintLogEvent(LogEvent logEvent) } sb.Append(msg) - .Append("Please take a look at the logging call where this occurred and fix your format string."); + .Append(" Please take a look at the logging call where this occurred and fix your format string."); StandardOutWriter.WriteLine(sb.ToString(), ErrorColor); } diff --git a/src/core/Akka/IO/Buffers/DirectBufferPool.cs b/src/core/Akka/IO/Buffers/DirectBufferPool.cs index 48095da2706..1e5315913bc 100644 --- a/src/core/Akka/IO/Buffers/DirectBufferPool.cs +++ b/src/core/Akka/IO/Buffers/DirectBufferPool.cs @@ -21,6 +21,16 @@ public class BufferPoolAllocationException : AkkaException public BufferPoolAllocationException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected BufferPoolAllocationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } /// diff --git a/src/core/Akka/Pattern/IllegalStateException.cs b/src/core/Akka/Pattern/IllegalStateException.cs index 22b337294da..d2a6c19ba94 100644 --- a/src/core/Akka/Pattern/IllegalStateException.cs +++ b/src/core/Akka/Pattern/IllegalStateException.cs @@ -33,7 +33,6 @@ public IllegalStateException(string message, Exception innerEx) : base(message, { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -43,6 +42,5 @@ protected IllegalStateException(SerializationInfo info, StreamingContext context : base(info, context) { } -#endif } } diff --git a/src/core/Akka/Pattern/OpenCircuitException.cs b/src/core/Akka/Pattern/OpenCircuitException.cs index 1743595eba1..14ebda07378 100644 --- a/src/core/Akka/Pattern/OpenCircuitException.cs +++ b/src/core/Akka/Pattern/OpenCircuitException.cs @@ -82,7 +82,6 @@ public OpenCircuitException(Exception cause, TimeSpan remainingDuration) : this("Circuit Breaker is open; calls are failing fast", cause, remainingDuration) { } -#if SERIALIZATION /// /// Initializes a new instance of the class. /// @@ -91,7 +90,15 @@ public OpenCircuitException(Exception cause, TimeSpan remainingDuration) protected OpenCircuitException(SerializationInfo info, StreamingContext context) : base(info, context) { + var duration = (string)info.GetValue("RemainingDuration", typeof(string)); + RemainingDuration = TimeSpan.Parse(duration); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) throw new ArgumentNullException(nameof(info)); + info.AddValue("RemainingDuration", RemainingDuration); + base.GetObjectData(info, context); } -#endif } } diff --git a/src/core/Akka/Pattern/UserCalledFailException.cs b/src/core/Akka/Pattern/UserCalledFailException.cs index b07fa431fa4..86260140a3e 100644 --- a/src/core/Akka/Pattern/UserCalledFailException.cs +++ b/src/core/Akka/Pattern/UserCalledFailException.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using System.Text; using Akka.Actor; @@ -16,5 +17,12 @@ public class UserCalledFailException : AkkaException { public UserCalledFailException() : base($"User code caused [{nameof(CircuitBreaker)}] to fail because it calls the [{nameof(CircuitBreaker.Fail)}()] method.") { } + + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected UserCalledFailException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } diff --git a/src/core/Akka/Routing/TailChopping.cs b/src/core/Akka/Routing/TailChopping.cs index c0704b2f940..a54f59d32cb 100644 --- a/src/core/Akka/Routing/TailChopping.cs +++ b/src/core/Akka/Routing/TailChopping.cs @@ -122,7 +122,14 @@ public override void Send(object message, IActorRef sender) try { - completion.TrySetResult(await (_routees[currentIndex].Ask(message, _within)).ConfigureAwait(false)); + completion.TrySetResult( + await (_routees[currentIndex].Ask(message, _within)).ConfigureAwait(false)); + } + catch (AskTimeoutException) + { + completion.TrySetResult( + new Status.Failure( + new AskTimeoutException($"Ask timed out on {sender} after {_within}"))); } catch (TaskCanceledException) { diff --git a/src/core/Akka/Util/MatchHandler/CachedMatchCompiler.cs b/src/core/Akka/Util/MatchHandler/CachedMatchCompiler.cs index 45daa4b7594..48b3e5c72df 100644 --- a/src/core/Akka/Util/MatchHandler/CachedMatchCompiler.cs +++ b/src/core/Akka/Util/MatchHandler/CachedMatchCompiler.cs @@ -71,27 +71,6 @@ private Delegate CompileToDelegate(IReadOnlyList handlers, IReadOnl delegateArguments = result.Arguments; return compiledLambda; } - -#if !CORECLR - /// - /// TBD - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - public void CompileToMethod(IReadOnlyList handlers, IReadOnlyList capturedArguments, MatchBuilderSignature signature, TypeBuilder typeBuilder, string methodName, MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.Static) - { - var result = _expressionBuilder.BuildLambdaExpression(handlers); - var lambdaExpression = result.LambdaExpression; - var parameterTypes = lambdaExpression.Parameters.Select(p => p.Type).ToArray(); - var method = typeBuilder.DefineMethod(methodName, methodAttributes, typeof(bool), parameterTypes); - _expressionCompiler.CompileToMethod(lambdaExpression, method); - } -#endif } } diff --git a/src/core/Akka/Util/MatchHandler/ILambdaExpressionCompiler.cs b/src/core/Akka/Util/MatchHandler/ILambdaExpressionCompiler.cs index 1a874995707..e7ee61bbfc1 100644 --- a/src/core/Akka/Util/MatchHandler/ILambdaExpressionCompiler.cs +++ b/src/core/Akka/Util/MatchHandler/ILambdaExpressionCompiler.cs @@ -22,15 +22,6 @@ internal interface ILambdaExpressionCompiler /// The expression to compile /// A delegate containing the compiled version of the lambda. Delegate Compile(LambdaExpression expression); - -#if !CORECLR - /// - /// Compiles the lambda into a method definition. - /// - /// The expression to compile - /// A which will be used to hold the lambda's IL. - void CompileToMethod(LambdaExpression expression, MethodBuilder method); -#endif } } diff --git a/src/core/Akka/Util/MatchHandler/IMatchCompiler.cs b/src/core/Akka/Util/MatchHandler/IMatchCompiler.cs index 4d597cf4b13..b06e99381f3 100644 --- a/src/core/Akka/Util/MatchHandler/IMatchCompiler.cs +++ b/src/core/Akka/Util/MatchHandler/IMatchCompiler.cs @@ -25,20 +25,6 @@ internal interface IMatchCompiler /// TBD /// TBD PartialAction Compile(IReadOnlyList handlers, IReadOnlyList capturedArguments, MatchBuilderSignature signature); - -#if !CORECLR - /// - /// TBD - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - void CompileToMethod(IReadOnlyList handlers, IReadOnlyList capturedArguments, MatchBuilderSignature signature, TypeBuilder typeBuilder, string methodName, MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.Static); -#endif } } diff --git a/src/core/Akka/Util/MatchHandler/LambdaExpressionCompiler.cs b/src/core/Akka/Util/MatchHandler/LambdaExpressionCompiler.cs index 205bb2a8f92..c1357d07a8f 100644 --- a/src/core/Akka/Util/MatchHandler/LambdaExpressionCompiler.cs +++ b/src/core/Akka/Util/MatchHandler/LambdaExpressionCompiler.cs @@ -25,19 +25,6 @@ public Delegate Compile(LambdaExpression expression) { return expression.Compile(); } - -#if !CORECLR - /// - /// TBD - /// - /// TBD - /// - /// TBD - public void CompileToMethod(LambdaExpression expression, MethodBuilder method) - { - expression.CompileToMethod(method); - } -#endif } } diff --git a/src/core/Akka/Util/MatchHandler/MatchBuilder.cs b/src/core/Akka/Util/MatchHandler/MatchBuilder.cs index 40ed83b6a2d..62b415b79ec 100644 --- a/src/core/Akka/Util/MatchHandler/MatchBuilder.cs +++ b/src/core/Akka/Util/MatchHandler/MatchBuilder.cs @@ -171,21 +171,6 @@ public PartialAction Build() return partialAction; } -#if !CORECLR - /// - /// TBD - /// - /// TBD - /// TBD - /// TBD - /// TBD - public void BuildToMethod(TypeBuilder typeBuilder, string methodName, MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.Static) - { - _compiler.CompileToMethod(_typeHandlers, _arguments, new MatchBuilderSignature(_signature), typeBuilder, methodName, methodAttributes: attributes); - _state = State.Built; - } -#endif - private static void EnsureCanHandleType(Type handlesType) { if(!_itemType.IsAssignableFrom(handlesType)) diff --git a/src/core/Akka/Util/RuntimeDetector.cs b/src/core/Akka/Util/RuntimeDetector.cs index 7953d416f70..11190800623 100644 --- a/src/core/Akka/Util/RuntimeDetector.cs +++ b/src/core/Akka/Util/RuntimeDetector.cs @@ -37,12 +37,7 @@ public static class RuntimeDetector /// true if the current runtime is Windows private static bool _IsWindows() { -#if CORECLR - return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#else - return System.Environment.OSVersion.Platform != PlatformID.MacOSX && - System.Environment.OSVersion.Platform != PlatformID.Unix; -#endif + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } } }