Skip to content

Releases: OrleansContrib/Orleankka

1.0.1

29 Jun 12:09
Compare
Choose a tag to compare

Changes

  • Updated to Orleans 1.4.2
  • Removed confusing overload of Ask from typed ActorRef

Orleankka 1.0.0-alpha2

18 Apr 08:01
Compare
Choose a tag to compare
Pre-release

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 receive mapping 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 inside Done() but rather via separate Connect(retries) method. Check whether it was really connected is possible via provided Connected() method
  • ClusterActorSystem don't start cluster inside Done() but rather via separate Start() method 848254a
  • ClusterActorSystem don't stop cluster inside Dispose() but rather via separate Stop(force) method.
  • ClientActorSystem \ ClusterActorSystem are not IDisposable 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 to ClientObservable 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 to Bootstrapper.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 35c8115
  • Reentrant 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

  • Option to roundtrip message serialization 78192bc
  • Added missing Notify expectations to ref mocks a51b1c4
  • Added typed actor ref mock c097311
  • Added recording of timer/reminder requests f7d18c8
  • Added basic storage mock for testing stateful actors 205b64d

Orleankka 0.10.10 (stable)

12 Nov 21:57
Compare
Choose a tag to compare

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)

31 Oct 23:25
Compare
Choose a tag to compare
Pre-release

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

28 Oct 10:35
Compare
Choose a tag to compare

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

13 Oct 18:29
Compare
Choose a tag to compare

New features

  • Stream subscriptions now support content based-filtering. Respective methods on StreamRef now take optional delegate parameter of type Func<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 additional Filter 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 and AzureQueueStreamProvider. To work correctly, stream providers still need to be configured programatically (using Register<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 to Subscribe().

Orleankka v0.10.6 (stable)

08 Oct 18:58
Compare
Choose a tag to compare

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 for Tell() in R#). See example of strongly typed actors here. You willl need to use ActorSystem.TypedActorRef<TActor> extension method in conjunction with ActorMessage<TActor> and ActorMessage<TActor, TResult> interfaces to get this behavior. Also, ActorRef<TActor> could be easily (implicitly) converted to weakly-typed ActorRef 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.

    1. OnNextAsync(object item) was renamed to Push(object item).
    2. OnCompletedAsync() and OnErrorAsync(Exception ex) methods were removed from StreamRef. 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, ie stream.Push(new Error("blah")).
    3. SubscribeAsync(...) was renamed to Subscribe(...). A number of overloads were added that allow to subscribe in a most convenient way (async/non-async).
    4. Instead of returning native Orleans StreamSubscriptionHandle<T> the Subscribe() now returns special StreamSubscription object that has a single Task Unsubscribe() method. Read section below to understand why there is no Resume() method anymore.
    5. GetAllSubscriptionHandles() method was removed from StreamRef. 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 to this. That will subscribe the actor's OnReceive() function as a callback. While you still need to resume stream subscription on actor activation, there is no need to do any special gymnastics with GetAllSubscritionHandles() anymore, since Orleankka will handle that part automatically for you.

    Moreover, both Subscribe() and Unsubscribe() 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 for Unsubscribe() - 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 id INV-001 provided by sms 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 id INV-001 and INV-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 of INV-:

    [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, where xxx 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 to ClientObserver 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 same IMessageSerializer that is used at run-time.
  • ActorSystemMock now returns ActorRefMock for unexpected calls as well as for expected. ActorRefStub was deleted since its behavior is already covered by ActorRefMock.
  • CommandExpectation and QueryExpectation were renamed to TellExpectation and AskExpectation respectively.
  • StreamRefMock was added to facilitate unit testing of stream interactions (pushes) with expectation setup/matching capabilities similar to ActorRef.
  • Fixed bug with incorrect mirroring of run-time behavior for TimerServiceMock and ReminderServiceMock.
  • Added Reset() method to all mocks.
  • Consistent naming for RecordedXXX properties.

Orleankka.Azure

  • Possibility to programatically configure DeploymentId and DataConnectionString (thanks to @mhertis)

Orleankka v0.10.1 (stable)

07 Oct 17:26
Compare
Choose a tag to compare

Orleans dependencies were update to 1.0.10 release