예제 #1
0
        static string GetKeyFromTuple(OperationQueueItem item)
        {
            // NB: This method assumes that the input tuples only have a
            // single item, which the OperationQueue input methods guarantee
            switch (item.OperationType)
            {
            case OperationType.BulkInsertSqliteOperation:
                return((item.ParametersAsElements).First().Key);

            case OperationType.BulkInvalidateByTypeSqliteOperation:
            case OperationType.BulkInvalidateSqliteOperation:
            case OperationType.BulkSelectSqliteOperation:
            case OperationType.BulkSelectByTypeSqliteOperation:
                return((item.ParametersAsKeys).First());

            case OperationType.GetKeysSqliteOperation:
            case OperationType.InvalidateAllSqliteOperation:
            case OperationType.VacuumSqliteOperation:
            case OperationType.DeleteExpiredSqliteOperation:
            case OperationType.DoNothing:
                return(default(string));

            default:
                throw new ArgumentException("Unknown operation");
            }
        }
예제 #2
0
        public AsyncSubject <Unit> Vacuum()
        {
            // Vacuum is a special snowflake. We want to delete all the expired rows before
            // actually vacuuming. Unfortunately vacuum can't be run in a transaction so we'll
            // claim an exclusive lock on the queue, drain it and run the delete first before
            // running our vacuum op without any transactions.
            var ret = new AsyncSubject <Unit>();

            Task.Run(async() =>
            {
                IDisposable @lock = null;
                try
                {
                    // NB. While the documentation for SemaphoreSlim (which powers AsyncLock)
                    // doesn't guarantee ordering the actual (current) implementation[1]
                    // uses a linked list to queue incoming requests so by adding ourselves
                    // to the queue first and then sending a no-op to the main queue to
                    // force it to finish up and release the lock we avoid any potential
                    // race condition where the main queue reclaims the lock before we
                    // have had a chance to acquire it.
                    //
                    // 1. http://referencesource.microsoft.com/#mscorlib/system/threading/SemaphoreSlim.cs,d57f52e0341a581f
                    var lockTask = flushLock.LockAsync(shouldQuit.Token);
                    operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                    try
                    {
                        @lock = await lockTask;
                    }
                    catch (OperationCanceledException) { }

                    var deleteOp = OperationQueueItem.CreateUnit(OperationType.DeleteExpiredSqliteOperation);
                    operationQueue.Add(deleteOp);

                    FlushInternal();

                    await deleteOp.CompletionAsUnit;

                    var vacuumOp = OperationQueueItem.CreateUnit(OperationType.VacuumSqliteOperation);

                    MarshalCompletion(vacuumOp.Completion, vacuum.Value.PrepareToExecute(), Observable.Return(Unit.Default));

                    await vacuumOp.CompletionAsUnit;
                }
                finally
                {
                    if (@lock != null)
                    {
                        @lock.Dispose();
                    }
                }
            })
            .ToObservable()
            .ObserveOn(scheduler)
            .Multicast(ret)
            .PermaRef();

            return(ret);
        }
예제 #3
0
        public AsyncSubject <IEnumerable <string> > GetAllKeys()
        {
            var ret = OperationQueueItem.CreateGetAllKeys();

            _operationQueue.Add(ret);

            return(ret.CompletionAsKeys);
        }
예제 #4
0
        public AsyncSubject <Unit> InvalidateAll()
        {
            var ret = OperationQueueItem.CreateUnit(OperationType.InvalidateAllSqliteOperation);

            _operationQueue.Add(ret);

            return(ret.CompletionAsUnit);
        }
예제 #5
0
        public AsyncSubject <Unit> InvalidateTypes(IEnumerable <string> types)
        {
            var ret = OperationQueueItem.CreateInvalidate(OperationType.BulkInvalidateByTypeSqliteOperation, types);

            _operationQueue.Add(ret);

            return(ret.CompletionAsUnit);
        }
예제 #6
0
        public AsyncSubject <Unit> Insert(IEnumerable <CacheElement> items)
        {
            var ret = OperationQueueItem.CreateInsert(OperationType.BulkInsertSqliteOperation, items);

            _operationQueue.Add(ret);

            return(ret.CompletionAsUnit);
        }
예제 #7
0
        public AsyncSubject <IEnumerable <CacheElement> > SelectTypes(IEnumerable <string> types)
        {
            var ret = OperationQueueItem.CreateSelect(OperationType.BulkSelectByTypeSqliteOperation, types);

            _operationQueue.Add(ret);

            return(ret.CompletionAsElements);
        }
예제 #8
0
        public IObservable <Unit> Flush()
        {
            var noop = OperationQueueItem.CreateUnit(OperationType.DoNothing);

            _operationQueue.Add(noop);

            return(noop.CompletionAsUnit);
        }
예제 #9
0
        public AsyncSubject <Unit> Vacuum()
        {
            var ret = OperationQueueItem.CreateUnit(OperationType.VacuumSqliteOperation);

            operationQueue.Add(ret);

            return(ret.CompletionAsUnit);
        }
예제 #10
0
        static OperationQueueItem GroupUnrelatedSelects(IEnumerable <OperationQueueItem> unrelatedSelects)
        {
            var elementMap = new Dictionary <string, AsyncSubject <IEnumerable <CacheElement> > >();

            if (unrelatedSelects.Count() == 1)
            {
                return(unrelatedSelects.First());
            }

            foreach (var v in unrelatedSelects)
            {
                var key = v.ParametersAsKeys.First();
                elementMap[key] = v.CompletionAsElements;
            }

            var ret = OperationQueueItem.CreateSelect(OperationType.BulkSelectSqliteOperation, elementMap.Keys);

            ret.CompletionAsElements.Subscribe(items =>
            {
                var resultMap = items.ToDictionary(k => k.Key, v => v);
                foreach (var v in elementMap.Keys)
                {
                    try {
                        if (resultMap.ContainsKey(v))
                        {
                            elementMap[v].OnNext(EnumerableEx.Return(resultMap[v]));
                        }
                        else
                        {
                            elementMap[v].OnNext(Enumerable.Empty <CacheElement>());
                        }

                        elementMap[v].OnCompleted();
                    }
                    catch (KeyNotFoundException e) {
                        // I don't know what to do here but since an exception is swallowed anyway,
                        // lets not stop the remaining elements to be stuck in an incompleted way
                    }
                }
            },
                                               ex =>
            {
                foreach (var v in elementMap.Values)
                {
                    v.OnError(ex);
                }
            },
                                               () =>
            {
                foreach (var v in elementMap.Values)
                {
                    v.OnCompleted();
                }
            });

            return(ret);
        }
예제 #11
0
        static OperationQueueItem GroupUnrelatedSelects(IEnumerable <OperationQueueItem> unrelatedSelects)
        {
            var elementMap = new Dictionary <string, AsyncSubject <IEnumerable <CacheElement> > >();

            if (unrelatedSelects.Count() == 1)
            {
                return(unrelatedSelects.First());
            }

            foreach (var v in unrelatedSelects)
            {
                var key = v.ParametersAsKeys.First();
                elementMap[key] = v.CompletionAsElements;
            }

            var ret = OperationQueueItem.CreateSelect(OperationType.BulkSelectSqliteOperation, elementMap.Keys);

            ret.CompletionAsElements.Subscribe(items =>
            {
                var resultMap = items.ToDictionary(k => k.Key, v => v);
                foreach (var v in elementMap.Keys)
                {
                    if (resultMap.ContainsKey(v))
                    {
                        elementMap[v].OnNext(EnumerableEx.Return(resultMap[v]));
                    }
                    else
                    {
                        elementMap[v].OnNext(Enumerable.Empty <CacheElement>());
                    }

                    elementMap[v].OnCompleted();
                }
            },
                                               ex =>
            {
                foreach (var v in elementMap.Values)
                {
                    v.OnError(ex);
                }
            },
                                               () =>
            {
                foreach (var v in elementMap.Values)
                {
                    v.OnCompleted();
                }
            });

            return(ret);
        }
예제 #12
0
        static OperationQueueItem GroupUnrelatedInserts(IEnumerable <OperationQueueItem> unrelatedInserts)
        {
            if (unrelatedInserts.Count() == 1)
            {
                return(unrelatedInserts.First());
            }

            var subj     = new AsyncSubject <Unit>();
            var elements = unrelatedInserts.SelectMany(x =>
            {
                subj.Subscribe(x.CompletionAsUnit);
                return(x.ParametersAsElements);
            }).ToList();

            return(OperationQueueItem.CreateInsert(
                       OperationType.BulkInsertSqliteOperation, elements, subj));
        }
예제 #13
0
        public IObservable <Unit> Flush()
        {
            var ret = new AsyncSubject <Unit>();

            return(Task.Run(async() =>
            {
                // NB: We add a "DoNothing" operation so that the thread waiting
                // on an item will always have one instead of waiting the full timeout
                // before we can run the flush
                operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                using (await flushLock.LockAsync())
                {
                    FlushInternal();
                }
            }).ToObservable());
        }
예제 #14
0
        private static OperationQueueItem GroupUnrelatedDeletes(IEnumerable <OperationQueueItem> unrelatedDeletes)
        {
            var subj = new AsyncSubject <Unit>();
            var operationQueueItems = unrelatedDeletes.ToList();

            if (operationQueueItems.Count == 1)
            {
                return(operationQueueItems[0]);
            }

            var elements = operationQueueItems.SelectMany(x =>
            {
                subj.Subscribe(x.CompletionAsUnit);
                return(x.ParametersAsKeys);
            }).ToList();

            return(OperationQueueItem.CreateInvalidate(
                       OperationType.BulkInvalidateSqliteOperation, elements, subj));
        }
예제 #15
0
        public IObservable <Unit> Flush()
        {
            var ret = new AsyncSubject <Unit>();

            return(Task.Run(async() =>
            {
                // NB: We add a "DoNothing" operation so that the thread waiting
                // on an item will always have one instead of waiting the full timeout
                // before we can run the flush
                operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                using (await flushLock.LockAsync())
                {
                    var newQueue = new BlockingCollection <OperationQueueItem>();
                    var existingItems = Interlocked.Exchange(ref operationQueue, newQueue).ToList();

                    ProcessItems(CoalesceOperations(existingItems));
                }
            }).ToObservable());
        }
예제 #16
0
        public AsyncSubject <Unit> Vacuum()
        {
            // Vacuum is a special snowflake. We want to delete all the expired rows before
            // actually vacuuming. Unfortunately vacuum can't be run in a transaction so we'll
            // claim an exclusive lock on the queue, drain it and run the delete first before
            // running our vacuum op without any transactions.
            var ret = new AsyncSubject <Unit>();

            Task.Run(async() =>
            {
                // NB: We add a "DoNothing" operation so that the thread waiting
                // on an item will always have one instead of waiting the full timeout
                // before we can run the flush
                operationQueue.Add(OperationQueueItem.CreateUnit(OperationType.DoNothing));

                using (await flushLock.LockAsync())
                {
                    var deleteOp = OperationQueueItem.CreateUnit(OperationType.DeleteExpiredSqliteOperation);
                    operationQueue.Add(deleteOp);

                    FlushInternal();

                    await deleteOp.CompletionAsUnit;

                    var vacuumOp = OperationQueueItem.CreateUnit(OperationType.VacuumSqliteOperation);

                    MarshalCompletion(vacuumOp.Completion, vacuum.PrepareToExecute(), Observable.Return(Unit.Default));

                    await vacuumOp.CompletionAsUnit;
                }
            })
            .ToObservable()
            .ObserveOn(scheduler)
            .Multicast(ret)
            .PermaRef();

            return(ret);
        }