RX: Detecting the end of a rapid series of events

Storing application state and user preferences is a pretty common problem.  And knowing when to save them can be a tricky problem in a distributed application (such as a Silverlight client) … save too often and you will use too much bandwidth just for saving preferences, save too infrequently or irregularly and users might not get their preferences saved at all.

Now I have a service for holding and saving user preferences, which is a pretty common situation.  It is notified when the saved preferences are dirty (e.g. as the user changes values on a form, say) and it has to decide when to actually collect and save all the values.  Now my service uses the Reactive Framework (RX) internally and Andy (a fellow RX junkie) insisted I post this:

public class UserPreferenceService : IUserPreferenceService
{
    private readonly TimeSpan _cooloffPeriod = TimeSpan.FromSeconds(10);
    private ISubject<string> _changeNotification;
    
    public void PreferencesAreStale()
    {
        _changeNotification.OnNext( null );
    }

    private void BeginListeningToChangeNotifications()
    {
        _changeNotification = new Subject<string>();

        // We apply a timestamp to all changes.  Then we delay these changes and recombine them with the latest changes.
        //      Comparing these timestamps allows us to work out if there have been any subsequent changes during the 
        //      delay period.  We allow some leeway (50ms) in the timestamps, since the two observable stream are delayed
        //      execution and so can get timestamped slightly differently.

        var timestampedChanges = _changeNotification.Timestamp();
        var delayedChanges = timestampedChanges.Delay( _cooloffPeriod );

        timestampedChanges
            .CombineLatest( delayedChanges, ( latest, delayed ) => latest.Timestamp.Subtract( delayed.Timestamp ) )
            .Where( timeDifference => timeDifference.TotalMilliseconds < 50 )
            .Subscribe( _ => PersistPreferences() );
    }

    private void PersistPreferences()
    {
        // Collects and saves preferences
    }
}

 

I think the comments are pretty clear with regard to the implementation details, but the overall result is that preferences get saved 10 seconds after the last update in a rapid series and no sooner.  Updates coming in more frequently than one every 10 seconds do not trigger a save operation.

November 21 2010
blog comments powered by Disqus