Beispiel #1
0
        public CollapsedRequest <RequestResponseType, RequestArgumentType> SubmitRequest(RequestArgumentType arg, CancellationToken token)
        {
            /*
             * We only want the timer ticking if there are actually things to do so we register it the first time something is added.
             */
            if (!timerListenerRegistered.Value && timerListenerRegistered.CompareAndSet(false, true))
            {
                /* schedule the collapsing task to be executed every x milliseconds (x defined inside CollapsedTask) */
                timerListenerReference.Value = timer.AddListener(new CollapsedTask <BatchReturnType, RequestResponseType, RequestArgumentType>(this));
            }

            // loop until succeed (compare-and-set spin-loop)
            while (true)
            {
                RequestBatch <BatchReturnType, RequestResponseType, RequestArgumentType> b = batch.Value;
                if (b == null)
                {
                    throw new InvalidOperationException("Submitting requests after collapser is shutdown");
                }

                CollapsedRequest <RequestResponseType, RequestArgumentType> response = b.Offer(arg, token);

                // it will always get an CollapsedRequest unless we hit the max batch size
                if (response != null)
                {
                    return(response);
                }
                else
                {
                    // this batch can't accept requests so create a new one and set it if another thread doesn't beat us
                    CreateNewBatchAndExecutePreviousIfNeeded(b);
                }
            }
        }
Beispiel #2
0
        /**
         * Best-effort attempt to remove an argument from a batch.  This may get invoked when a cancellation occurs somewhere downstream.
         * This method finds the argument in the batch, and removes it.
         *
         */

        internal void Remove(RequestArgumentType arg)
        {
            if (batchStarted.Value)
            {
                //nothing we can do
                return;
            }

            if (batchLock.TryEnterReadLock(1))
            {
                try
                {
                    /* double-check now that we have the lock - if the batch is started, deleting is useless */
                    if (batchStarted.Value)
                    {
                        return;
                    }
                    CollapsedRequest <RequestResponseType, RequestArgumentType> existing = null;
                    if (arg == null)
                    {
                        nullArg.Value = null;
                    }

                    if (argumentMap.TryRemove(arg, out existing))
                    {
                        // Log
                    }
                }
                finally
                {
                    batchLock.ExitReadLock();
                }
            }
        }
        public CollapsedRequest <RequestResponseType, RequestArgumentType> Offer(RequestArgumentType arg, CancellationToken token)
        {
            /* short-cut - if the batch is started we reject the offer */
            if (batchStarted.Value)
            {
                return(null);
            }

            /*
             * The 'read' just means non-exclusive even though we are writing.
             */
            if (batchLock.TryEnterReadLock(1))
            {
                try
                {
                    /* double-check now that we have the lock - if the batch is started we reject the offer */
                    if (batchStarted.Value)
                    {
                        return(null);
                    }

                    if (argumentMap.Count >= maxBatchSize)
                    {
                        return(null);
                    }
                    else
                    {
                        CollapsedRequest <RequestResponseType, RequestArgumentType> collapsedRequest = new CollapsedRequest <RequestResponseType, RequestArgumentType>(arg, token);
                        TaskCompletionSource <RequestResponseType> tcs = new TaskCompletionSource <RequestResponseType>(collapsedRequest);
                        collapsedRequest.CompletionSource = tcs;

                        CollapsedRequest <RequestResponseType, RequestArgumentType> existing = null;

                        if (arg == null)
                        {
                            existing = GetOrAddNullArg(collapsedRequest);
                        }
                        else
                        {
                            existing = argumentMap.GetOrAdd(arg, collapsedRequest);
                        }

                        /*
                         * If the argument already exists in the batch, then there are 2 options:
                         * A) If request caching is ON (the default): only keep 1 argument in the batch and let all responses
                         * be hooked up to that argument
                         * B) If request caching is OFF: return an error to all duplicate argument requests
                         *
                         * This maintains the invariant that each batch has no duplicate arguments.  This prevents the impossible
                         * logic (in a user-provided mapResponseToRequests for HystrixCollapser and the internals of HystrixObservableCollapser)
                         * of trying to figure out which argument of a set of duplicates should get attached to a response.
                         *
                         * See https://github.com/Netflix/Hystrix/pull/1176 for further discussion.
                         */
                        if (existing != collapsedRequest)
                        {
                            bool requestCachingEnabled = properties.RequestCacheEnabled;
                            if (requestCachingEnabled)
                            {
                                return(existing);
                            }
                            else
                            {
                                throw new ArgumentException("Duplicate argument in collapser batch : [" + arg + "]  This is not supported.  Please turn request-caching on for HystrixCollapser:" + commandCollapser.CollapserKey.Name + " or prevent duplicates from making it into the batch!");
                            }
                        }
                        else
                        {
                            return(collapsedRequest);
                        }
                    }
                }
                finally
                {
                    batchLock.ExitReadLock();
                }
            }
            else
            {
                return(null);
            }
        }
        internal CollapsedRequest <RequestResponseType, RequestArgumentType> GetOrAddNullArg(CollapsedRequest <RequestResponseType, RequestArgumentType> collapsedRequest)
        {
            if (nullArg.CompareAndSet(null, collapsedRequest))
            {
                return(collapsedRequest);
            }

            return(nullArg.Value);
        }