On Microsoft’s //build2014 conference, Project Orleans had it’s public debut. You can watch the introductory video here, and also download the bits.

In Orleans, a grain can have state, and when the grain is activated or deactivated (removed from memory), this state is automatically loaded / stored. Method on the grain can also save the grain’s state to the underlying persistency provider when they think is a good time, e.g. when there were significant changes:

public Task Promote(int newLevel)
{
    this.State.Level = newLevel;
    return this.State.WriteStateAsync();
}

I was looking for a clean way to save the state on a regular basis, instead of during each invocation. This is done my a simple utility class, the ‘OrleansStatePersistencyPolicy.cs’: In my grain, I initialize this persistency policy. In this example, if there were fewer than 10 seconds before last invocation, the state is not saved:

namespace MyGrainCollection
{
    using System;
    using System.Threading.Tasks;
    using Orleans;
    using MyGrainInterfaces;

    [StorageProvider(ProviderName = "AzureTableStorage")]
    public class MyGrain : GrainBase<IMyGrainState>, IMyGrain
    {
        private readonly OrleansStatePersistencyPolicy policy = OrleansStatePersistencyPolicy.Every(TimeSpan.FromSeconds(10));

        async Task<int> IMyGrain.GetQuote()
        {
            this.State.Value++;

            // await this.State.WriteStateAsync();

            await this.policy.PersistIfNeeded(
                persist: this.State.WriteStateAsync);

            return this.State.Value;
        }
    }
}

Here’s the simple functional approach for the persistency policy:

namespace MyGrainCollection
{
    using System;
    using System.Threading.Tasks;

    public class OrleansStatePersistencyPolicy
    {
        public static OrleansStatePersistencyPolicy Every(TimeSpan interval)
        {
            return new OrleansStatePersistencyPolicy(interval);
        }

        public OrleansStatePersistencyPolicy(TimeSpan interval)
        {
            this.Interval = interval;
        }

        private TimeSpan Interval { get; set; }

        private DateTimeOffset Last { get; set; }

        private bool ShouldPersist { get { return DateTimeOffset.UtcNow > this.Last.Add(this.Interval); } }

        public async Task PersistIfNeeded(Func<Task> persist)
        {
            if (ShouldPersist)
            {
                await persist();
                this.Last = DateTimeOffset.UtcNow;
            }
        }
    }
}
comments by Disqus