Пример #1
0
        public IChangeSet<TObject, TKey> Change(IChangeSet<ExpirableItem<TObject, TKey>, TKey> updates)
        {
            _cache.Clone(updates);

            var itemstoexpire = _cache.KeyValues
                                      .OrderByDescending(exp => exp.Value.ExpireAt)
                                      .Skip(_sizeLimit)
                                      .Select(exp => new Change<TObject, TKey>(ChangeReason.Remove, exp.Key, exp.Value.Value))
                                      .ToList();

            if (itemstoexpire.Count > 0)
            {
                _cache.Remove(itemstoexpire.Select(exp => exp.Key));
            }

            var notifications = _cache.CaptureChanges();
            var changed = notifications.Select(update => new Change<TObject, TKey>
                                                   (
                                                   update.Reason,
                                                   update.Key,
                                                   update.Current.Value,
                                                   update.Previous.HasValue ? update.Previous.Value.Value : Optional<TObject>.None
                                                   ));

            return new ChangeSet<TObject, TKey>(changed);
        }
Пример #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="LockFreeObservableCache{TObject, TKey}"/> class.
        /// </summary>
        /// <param name="source">The source.</param>
        public LockFreeObservableCache(IObservable <IChangeSet <TObject, TKey> > source)
        {
            _updater = new CacheUpdater <TObject, TKey>(_innerCache);

            var loader = source.Select(changes =>
            {
                _innerCache.Clone(changes);
                return(_innerCache.CaptureChanges());
            }).SubscribeSafe(_changes);

            _cleanUp = Disposable.Create(() =>
            {
                loader.Dispose();
                _changes.OnCompleted();
                _countChanged.OnCompleted();
            });
        }
Пример #3
0
        private IObservable <IChangeSet <TDestination, TDestinationKey> > CreateWithChangeSet()
        {
            if (_childChanges is null)
            {
                throw new InvalidOperationException("The childChanges is null and should not be.");
            }

            return(Observable.Create <IChangeSet <TDestination, TDestinationKey> >(
                       observer =>
            {
                var result = new ChangeAwareCache <TDestination, TDestinationKey>();

                var transformed = _source.Transform(
                    (t, _) =>
                {
                    // Only skip initial for first time Adds where there is initial data records
                    var locker = new object();
                    var collection = _manySelector(t);
                    var changes = _childChanges(t).Synchronize(locker).Skip(1);
                    return new ManyContainer(
                        () =>
                    {
                        lock (locker)
                        {
                            return collection.Select(m => new DestinationContainer(m, _keySelector(m))).ToArray();
                        }
                    },
                        changes);
                }).Publish();

                var outerLock = new object();
                var initial = transformed.Synchronize(outerLock).Select(changes => new ChangeSet <TDestination, TDestinationKey>(new DestinationEnumerator(changes)));

                var subsequent = transformed.MergeMany(x => x.Changes).Synchronize(outerLock);

                var allChanges = initial.Merge(subsequent).Select(
                    changes =>
                {
                    result.Clone(changes);
                    return result.CaptureChanges();
                });

                return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect());
            }));
        }
Пример #4
0
        public ChangeSet <TObject, TKey> Write(IChangeSet <TObject, TKey> changes, bool notifyChanges)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }
            ChangeSet <TObject, TKey> result;

            lock (_locker)
            {
                if (notifyChanges)
                {
                    _changeAwareCache.Clone(changes);
                    result = _changeAwareCache.CaptureChanges();
                }
                else
                {
                    _data.Clone(changes);
                    result = ChangeSet <TObject, TKey> .Empty;
                }
            }
            return(result);
        }
Пример #5
0
        private IObservable <IChangeSet <TDestination, TDestinationKey> > CreateWithChangeset()
        {
            return(Observable.Create <IChangeSet <TDestination, TDestinationKey> >(observer =>
            {
                var result = new ChangeAwareCache <TDestination, TDestinationKey>();


                var transformed = _source.Transform((t, key) =>
                {
                    var locker = new object();
                    var collection = _manyselector(t);
                    var changes = _childChanges(t).Synchronize(locker).Skip(1);
                    return new ManyContainer(() =>
                    {
                        lock (locker)
                            return collection.Select(m => new DestinationContainer(m, _keySelector(m)));
                    }, changes);
                }).Publish();

                var outerLock = new object();
                var intial = transformed
                             .Synchronize(outerLock)
                             .Select(changes => new ChangeSet <TDestination, TDestinationKey>(new DestinationEnumerator(changes)));

                var subsequent = transformed
                                 .MergeMany(x => x.Changes)
                                 .Synchronize(outerLock);

                var allChanges = intial.Merge(subsequent).Select(changes =>
                {
                    result.Clone(changes);
                    return result.CaptureChanges();
                });

                return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect());
            }));
        }
Пример #6
0
 public void Update(IChangeSet <TObject, TKey> changes)
 {
     _cache.Clone(changes);
 }
Пример #7
0
            /// <summary>
            /// Sorts using the specified sorter. Will return null if there are no changes
            /// </summary>
            /// <param name="sortReason">The sort reason.</param>
            /// <param name="changes">The changes.</param>
            /// <returns></returns>
            private ISortedChangeSet <TObject, TKey> DoSort(SortReason sortReason, IChangeSet <TObject, TKey> changes = null)
            {
                if (changes != null)
                {
                    _cache.Clone(changes);
                    changes           = _cache.CaptureChanges();
                    _haveReceivedData = true;
                    if (_comparer == null)
                    {
                        return(null);
                    }
                }

                //if the comparer is not set, return nothing
                if (_comparer == null || !_haveReceivedData)
                {
                    return(null);
                }

                if (!_initialised)
                {
                    sortReason   = SortReason.InitialLoad;
                    _initialised = true;
                }
                else if (changes != null && (_resetThreshold > 0 && changes.Count >= _resetThreshold))
                {
                    sortReason = SortReason.Reset;
                }

                IChangeSet <TObject, TKey> changeSet;

                switch (sortReason)
                {
                case SortReason.InitialLoad:
                {
                    //For the first batch, changes may have arrived before the comparer was set.
                    //therefore infer the first batch of changes from the cache
                    _calculator = new IndexCalculator <TObject, TKey>(_comparer, _optimisations);
                    changeSet   = _calculator.Load(_cache);
                }
                break;

                case SortReason.Reset:
                {
                    _calculator.Reset(_cache);
                    changeSet = changes;
                }
                break;

                case SortReason.DataChanged:
                {
                    changeSet = _calculator.Calculate(changes);
                }
                break;

                case SortReason.ComparerChanged:
                {
                    changeSet = _calculator.ChangeComparer(_comparer);
                    if (_resetThreshold > 0 && _cache.Count >= _resetThreshold)
                    {
                        sortReason = SortReason.Reset;
                        _calculator.Reset(_cache);
                    }
                    else
                    {
                        sortReason = SortReason.Reorder;
                        changeSet  = _calculator.Reorder();
                    }
                }
                break;

                case SortReason.Reorder:
                {
                    changeSet = _calculator.Reorder();
                }
                break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(sortReason));
                }

                Debug.Assert(changeSet != null, "changeSet != null");
                if ((sortReason == SortReason.InitialLoad || sortReason == SortReason.DataChanged) &&
                    changeSet.Count == 0)
                {
                    return(null);
                }

                if (sortReason == SortReason.Reorder && changeSet.Count == 0)
                {
                    return(null);
                }

                _sorted = new KeyValueCollection <TObject, TKey>((IReadOnlyCollection <KeyValuePair <TKey, TObject> >)_calculator.List, _comparer, sortReason, _optimisations);
                return(new SortedChangeSet <TObject, TKey>(_sorted, changeSet));
            }
Пример #8
0
        public IObservable <IChangeSet <TObject, TKey> > Run()
        {
            return(Observable.Create <IChangeSet <TObject, TKey> >
                   (
                       observer =>
            {
                var result = new ChangeAwareCache <TObject, TKey>();
                var locker = new object();
                var paused = _intialPauseState;
                var timeoutDisposer = new SerialDisposable();
                var intervalTimerDisposer = new SerialDisposable();

                void ResumeAction()
                {
                    //publish changes (if there are any)
                    var changes = result.CaptureChanges();
                    if (changes.Count > 0)
                    {
                        observer.OnNext(changes);
                    }
                }

                IDisposable IntervalFunction()
                {
                    return _intervalTimer
                    .Synchronize(locker)
                    .Finally(() => paused = false)
                    .Subscribe(_ =>
                    {
                        paused = false;
                        ResumeAction();
                        if (_intervalTimer != null)
                        {
                            paused = true;
                        }
                    });
                }

                if (_intervalTimer != null)
                {
                    intervalTimerDisposer.Disposable = IntervalFunction();
                }

                var pausedHander = _pauseIfTrueSelector
                                   // .StartWith(initalp)
                                   .Synchronize(locker)
                                   .Subscribe(p =>
                {
                    paused = p;
                    if (!p)
                    {
                        //pause window has closed, so reset timer
                        if (_timeOut.HasValue)
                        {
                            timeoutDisposer.Disposable = Disposable.Empty;
                        }
                        ResumeAction();
                    }
                    else
                    {
                        if (_timeOut.HasValue)
                        {
                            timeoutDisposer.Disposable = Observable.Timer(_timeOut.Value, _scheduler)
                                                         .Synchronize(locker)
                                                         .Subscribe(_ =>
                            {
                                paused = false;
                                ResumeAction();
                            });
                        }
                    }
                });

                var publisher = _source
                                .Synchronize(locker)
                                .Subscribe(changes =>
                {
                    result.Clone(changes);

                    //publish if not paused
                    if (!paused)
                    {
                        observer.OnNext(result.CaptureChanges());
                    }
                });

                return new CompositeDisposable(publisher, pausedHander, timeoutDisposer, intervalTimerDisposer);
            }
                   ));
        }