Releases: OrleansContrib/Orleankka
1.0.1
Orleankka 1.0.0-alpha2
Changes since 0.23
Breaking
- Clean client/server separation. You will need to reference Orleankka.Runtime (and Orleankka.FSharp.Runtime if using F# api) package on the server-side. Clients need to reference only Orleankka (and Orleankka.FSharp respectively) core package.
- C# Clients should register assemblies with message definitions and marker interface (see Chat examples) in order to use TypedActor references, e.g.
clientSystem.ActorOf<IMyActor>
. Projects using Playground will fetch old style definitions inheriting Actor. You will receivemapping not found exception
if marker interface was not found by ClientActorSystem. - F# clients can only reference via actor type code and have to reference assembly containing message definitions.
[ActorTypeCode]
attribute has been renamed to[ActorType]
- Separate Orleankka.Azure package with Azure-related configuration has been deprecated in favor of simply configuring
ClusterActorSystem
with respective liveness type (AzureTable
) ClientActorSystem
don't connect to cluster insideDone()
but rather via separateConnect(retries)
method. Check whether it was really connected is possible via providedConnected()
methodClusterActorSystem
don't start cluster insideDone()
but rather via separateStart()
method 848254aClusterActorSystem
don't stop cluster insideDispose()
but rather via separateStop(force)
method.ClientActorSystem
\ClusterActorSystem
are notIDisposable
anymore- Since real grain interfaces are now generated, actor type is not included in grain id anymore. That's breaking for reminders and stream subscriptions. OrleansReminders and OrleansGrainState tables need to be cleaned up before deploying new version
RegisterXXX
prefix has been removed (RegisterStreamProvider<T>()
->StreamProvider<T>()
) f2044fd
Other changes
- Message serializers are gone in favor of Orleans built-in
IExternalSerializer
. Pull latest sources for examples on using ProtoBuf/Bond/Hyperion serializers with Orleankka ClientObserver
has been renamed toClientObservable
to match .NET reactive idioms #62- Client/Cluster actor system objects now provide access to underlying grain client and silo host
IActorSystem
is now passed as argument toBootstrapper.Run(system, properties)
Reentrant
attribute has been extended with overloads which allow to specify reentrancy by message type (multiple attributes are allowed), or by predicate callback function 35c8115Reentrant
attribute now works for messages delivered via stream- Declarative stream subscriptions are now server-side only (due to client/server separation)
- Stream subscription now return new stream subscription token on resume. Fixes #114
- Updated to Orleans 1.4
F# api changes since 0.23
- Added AwaitTask(Task -> Task) helper method
- Added TryFinally (body:unit -> Task<_>, compensation) helper method
- Added TryWith(m:CancellationToken -> Task<'T ..) helper method
- Added streaming api
- Aded client observable api
- Configuration api was extended to match C# version
New features
- One-off timers #51
- Autorun actors 669aa18
- Sticky actors 3834d81
- Custom handler naming conventions 605b736
- Silo/Actor-level interceptors #108
- Stateful Actors and Storage Providers (Orleans declarative persistence) eddb4c5
- FSM (switchable behaviors in Akka). Docs are coming. For now, check unit tests and/or see example in EventSourcing folder #111
TestKit
Orleankka 0.10.10 (stable)
New features
Actor-based stream subscriptions, now use "declared handlers only" stream filter by default. This means that only those items will be delivered, for which there is a corresponding message handler is declared by target actor. The filtering is done on a producer side.
class SomeConsumerActor : Actor
{
void On(SomeEvent1 e) {...}
void Subscribe()
{
System.StreamOf("sms", "11").Subscribe(this);
}
}
var stream = System.StreamOf("sms", "11");
await stream.Push(new SomeEvent1());
await stream.Push(new SomeEvent2()); // won't get HandlerNotFoundException as previously
This behavior is now default for both imperative and declarative subscriptions. If you want to receive all items from the stream (perhaps, you're overriding OnReceive
), you can still do it (see below).
For imperative stream subscriptions, use overload with additional parameter:
class SomeConsumerActor : Actor
{
void Subscribe()
{
System.StreamOf("sms", "11").Subscribe(this, StreamFilter.ReceiveAll);
}
protected override Task<object> ReceiveAll(object message)
{
// will now receive all items from stream "sms:11"
}
}
For declarative stream subscriptions, use *
symbol in filter specification:
[StreamSubscription(Source = "sms:11", Target = "#", Filter = "*")]
class SomeConsumerActor : Actor
{
protected override Task<object> ReceiveAll(object message)
{
// will now receive all items from stream "sms:11"
}
}
Api changes & bug fixes
The "declared handlers only" default stream filtering behavior is (semantically) breaking change. See above how to get "receive all" stream filter behavior which exists in previous versions.
Orleankka 0.10.9 (for early feedback)
New features
Added ability to choose stream subscription target (actor id) for every item dynamically. Syntax:
[StreamSubscription(Source = "sms:foo", Target = "ComputeTarget()")]
class SomeConsumerActor : Actor
{
static string ComputeTarget(object item) => "bar";
}
Note: ()
parenthesis need to be used, to specify that the value of Target should be treated as a function, rather than just a static value.
Api changes & fixes
Syntax for specifying predicate functions in StreamSubscription
Filter property was changed for consistency with Target string function specification syntax. Add ()
to the end of string to denote that value is a function.
[StreamSubscription(Source = "sms:foo", Filter = "SelectItem()")]
Orleankka v0.10.8
What's new
- Optimized
Tell
requests.
Bug fixes
- Fixed bug with actors that have multiple levels of inheritance. Dispatcher was gathering handlers multiple times while walking up the inheritance tree.
Orleankka v0.10.7
New features
-
Stream subscriptions now support content based-filtering. Respective methods on
StreamRef
now take optional delegate parameter of typeFunc<object, bool>
. This delegate should point to a static method. This constraint is due to filter delegate serialization requirement (durable pub-sub), which is done by serializing declaring type and method name. For declarative subscriptions the filter method name can be specified via additionalFilter
field:[StreamSubscription(Source = "sms:filtered", Target = "#", Filter = "Select")] class TestFilteredSubscriptionActor : Actor { public static bool Select(object item) => item is InventoryItemRenamed; ...
Api changes & bug fixes
-
Declarative stream subscriptions feature now work correctly with all built-in stream providers. It was tested against
SimpleMessageStreamProvider
andAzureQueueStreamProvider
. To work correctly, stream providers still need to be configured programatically (usingRegister<TStreamProvider>(name)
). -
Regex-based stream subscription now need to be explicitly indicated. Default behavior for treating the value of the Source field as a regex pattern was confusing and error-prone, since "sms:a" was also matching "sms:as", thus requiring regex anchors to be specified. To fix it, JavaScript-ish syntax was chosen as a way to indicate a regex matching pattern:
[StreamSubscription(Source = "sms:/INV-([0-9]+)/", Target = "#")] class TestRegexBasedSubscriptionActor : Actor
Orleankka.TestKit
StreamRefMock
was updated to record stream filter passed toSubscribe()
.
Orleankka v0.10.6 (stable)
New features
-
It happened! Typed actors finally arrived to Orleankka! That enabled compile-time safety and IntelliSense discovery of messages which are allowed to be sent to a particular actor (try
Ctrl-Shift-Space
smart completion forTell()
in R#). See example of strongly typed actors here. You willl need to useActorSystem.TypedActorRef<TActor>
extension method in conjunction withActorMessage<TActor>
andActorMessage<TActor, TResult>
interfaces to get this behavior. Also,ActorRef<TActor>
could be easily (implicitly) converted to weakly-typedActorRef
and back when needed. -
Streams api simplification. Previously, there was no any specially designed streams api in Orleankka and native Orleans api was exposed as-is. Unfortunately, native Orleans streams api is a convoluted monster that have a lot of useless and completely unnecessary stuff baked into it. Orleankka deserves something better, so the original streams api has undergone a major re-design in this release.
OnNextAsync(object item)
was renamed toPush(object item)
.OnCompletedAsync()
andOnErrorAsync(Exception ex)
methods were removed fromStreamRef
. It doesn't make much sense to propagate exceptions from producers to consumers. The semantics could be easily reproduced with a regular push api, simply by pushing a special item, iestream.Push(new Error("blah"))
.SubscribeAsync(...)
was renamed toSubscribe(...)
. A number of overloads were added that allow to subscribe in a most convenient way (async/non-async).- Instead of returning native Orleans
StreamSubscriptionHandle<T>
theSubscribe()
now returns specialStreamSubscription
object that has a singleTask Unsubscribe()
method. Read section below to understand why there is noResume()
method anymore. GetAllSubscriptionHandles()
method was removed fromStreamRef
. Actor subscription feature described below has made it largely irrelevant.
-
The delegate-based stream subscription api makes sense when used from client-side, not so much when used inside an actor. The cornerstone of Orleankka is uniform actor api and delegate-based stream subscription api breaks that premise somewhat. The actor already has a standard receive channel in form of
OnReceive(msg)
method that has nice dispatching capabilities by default, so why having more?The above thoughts and considerations led to the inception of actor subscription api in Orleankka. Basically, it all boils down to just a few additional methods on
StreamRef
but the implications are much more profound (see the description of declarative stream subscriptions feature).Without further ado:
class TestConsumerActor : Actor { readonly List<string> received = new List<string>(); Task On(Subscribe x) => Stream().Subscribe(this); Task On(Unsubscribe x) => Stream().Unsubscribe(this); void On(string x) => received.Add(x); List<string> On(Received x) => received; public override Task OnActivate() => Stream().Resume(this); StreamRef Stream() => System.StreamOf("sms", "42"); }
As you can see from the above code sample, instead of passing the delegate to
Subscribe
/Resume
functions we're passing the reference tothis
. That will subscribe the actor'sOnReceive()
function as a callback. While you still need to resume stream subscription on actor activation, there is no need to do any special gymnastics withGetAllSubscritionHandles()
anymore, since Orleankka will handle that part automatically for you.Moreover, both
Subscribe()
andUnsubscribe()
were made idempotent. That said, it doesn't matter how many times you subscribe an actor, there will be only one subscription created. Same goes forUnsubscribe()
- you can call it multiple times and it will not blow up with exception. Also,Resume()
could be safely called even if no subscription for this actor was previously made, which makes it safe to invoke during actor activation. -
Simplification of the streams api plus the ability to subscribe an actor to a stream, made possible development of another interesting and important feature - declarative stream subscriptions. In a nutshell, it allows to subscribe an actor to a stream by simply marking an actor class with special attribute. The subscription declaration have 2 mandatory fields: id of the source stream and id of the target actor. For example, the following code subscribes actor with id
#
to a stream with idINV-001
provided bysms
stream provider:[StreamSubscription(Source = "sms:INV-001", Target = "#")] class TestConsumerActor : Actor { void On(string x) => Console.WriteLine("Received:" + x); }
Notice, that you don't even need to call
Resume
during actor activation, everything is taken care for you automatically.You can declare multiple stream subscriptions for an actor, effectively subscribing to multiple streams. The following declaration subscribes actor with id
#
to streams with idINV-001
andINV-002
:[StreamSubscription(Source = "sms:INV-001", Target = "#")] [StreamSubscription(Source = "sms:INV-002", Target = "#")] class TestConsumerActor : Actor
That's cool, but what if you don't know stream ids in advance? Having the ability to subscribe to fixed stream ids is not enough. Well, no problem, declarative stream subscriptions allows you to specify regex matching patterns to handle such cases! The following code subscribes actor with id
invoices-total
to streams with ids having a prefix ofINV-
:[StreamSubscription(Source = "sms:INV-(.+)", Target = "invoices-total")] class TestConsumerActor : Actor
That's awesome but if we're dealing with a more complex scenario? Let's say we have a number of Accounts each having its own independent set of Invoices, and we want to compute a total of invoices per each account. The corresponding invoice streams are having the naming scheme which look like this:
ACC-123-INV-0001
. What we want is to have a number of actors, one per account, with ids like:ACC-xxx-total
, wherexxx
is the id of account encoded in a stream name of an invoice. How can we do it?Turns out, declarative stream subscriptions support placeholders in target actor ids that will be automatically mapped to named captures (groups) specified in the regex matching pattern.
[StreamSubscription(Source = "sms:ACC-(?<account>[^-]+)-(.+)", Target = "{account}-total")]
Cool, isn't it? 😄
WARNING: Declarative stream subscriptions feature was tested only against
SimpleMessageStreamProducer
and currently designed only to work with it. To work correctly, stream provider need to be configured programatically by using specialRegister<TStreamProvider>(name)
method. -
Dispatcher now supports private and inherited message handler methods. All dispatch methods were unified under single public
Task<TResult> Dispatch<TResult>(object message, Func<object, Task<object>> fallback = null)
dispatch function, which optionally allows to provide a callback, that will be invoked if handler for the message being dispatched is missing. It can be used both inside actor and also in a test harness to dispatch messages to private message handler methods. -
Stream refs were made equatable and serializable so they can be unit tested easily and passed around in the same way as actor refs.
API changes & fixes
-
Removed useless generics used to refer to
StreamProvider
when acquiring stream reference, since it doesn't provide much benefit. Changed to string based names like in native Orleans api.Was:
var stream = System.StreamOf<SimpleMessageStreamProvider>(room);
Now:
var stream = System.StreamOf("SMS", room);
-
Moved everything Meta (Command, Query, etc) into separate assembly. That will allow end-users to simply remove reference to this assembly to get rid of the hoisting problems in situations when similar meta interfaces are already in use by an application.
-
Dropped support for lambda-based message handlers (declared via prototypical
Define()
method). C# v6 expression-bodied members can be used instead. Otherwise, just rewrite to use vanilla C# methods if you have been using this feature. -
Made all
Actor
's protected methods public to simplify creation of extension methods. -
Renamed
Observer
toClientObserver
to better indicate its utility, ie client-side (outside silo) observable proxy. -
Switched to semicolon as actor uid delimiter, since slash was creating problems for reminders on Azure storage.
Orleankka.TestKit
ActorRefMock
can now force message serialization to check whether message could be successfully serialized/deserialized.ActorSystemMock
can be configured with the sameIMessageSerializer
that is used at run-time.ActorSystemMock
now returnsActorRefMock
for unexpected calls as well as for expected.ActorRefStub
was deleted since its behavior is already covered byActorRefMock
.CommandExpectation
andQueryExpectation
were renamed toTellExpectation
andAskExpectation
respectively.StreamRefMock
was added to facilitate unit testing of stream interactions (pushes) with expectation setup/matching capabilities similar toActorRef
.- Fixed bug with incorrect mirroring of run-time behavior for
TimerServiceMock
andReminderServiceMock
. - Added
Reset()
method to all mocks. - Consistent naming for
RecordedXXX
properties.
Orleankka.Azure
- Possibility to programatically configure
DeploymentId
andDataConnectionString
(thanks to @mhertis)
Orleankka v0.10.1 (stable)
Orleans dependencies were update to 1.0.10 release