Semaphore that only supports TryAcquire and never blocks and that supports a dynamic permit count.
Using AtomicInteger increment/decrement instead of java.util.concurrent.Semaphore since we don't need blocking and need a custom implementation to get the dynamic permit count and since AtomicInteger achieves the same behavior and performance without the more complex implementation of the actual Semaphore class using AbstractQueueSynchronizer.
Exemplo n.º 1
0
        public void Command_ExecutionHookFailureWithSemaphoreIsolation()
        {
            /* test with Execute() */
            TryableSemaphore semaphore =
                new TryableSemaphore(HystrixPropertyFactory.AsProperty(0));

            TestSemaphoreCommand command = new TestSemaphoreCommand(new TestCircuitBreaker(), semaphore, TimeSpan.FromMilliseconds(200));
            try
            {
                command.Execute();
                Assert.Fail("we expect a failure");
            }
            catch (Exception)
            {
                // expected
            }

            Assert.IsFalse(command.IsExecutedInThread);
            Assert.IsTrue(command.IsResponseRejected);

            // the run() method should not run as we are rejected
            Assert.AreEqual(0, command.Builder.ExecutionHook.StartRun);
            // null as run() does not get invoked
            Assert.IsNull(command.Builder.ExecutionHook.RunSuccessResponse);
            // null as run() does not get invoked
            Assert.IsNull(command.Builder.ExecutionHook.RunFailureException);

            // the fallback() method should run because of rejection
            Assert.AreEqual(1, command.Builder.ExecutionHook.StartFallback);
            // null since there is no fallback
            Assert.IsNull(command.Builder.ExecutionHook.FallbackSuccessResponse);
            // not null since the fallback is not implemented
            Assert.IsNotNull(command.Builder.ExecutionHook.FallbackFailureException);

            // the Execute() method was used
            Assert.AreEqual(1, command.Builder.ExecutionHook.StartExecute);
            // we should not have a response since fallback has nothing
            Assert.IsNull(command.Builder.ExecutionHook.EndExecuteSuccessResponse);
            // we won't have an exception because rejection doesn't have one
            Assert.IsNull(command.Builder.ExecutionHook.EndExecuteFailureException);
            // but we do expect to receive a onError call with FailureType.Shortcircuit
            Assert.AreEqual(FailureType.RejectedSemaphoreExecution, command.Builder.ExecutionHook.EndExecuteFailureType);

            // thread execution
            Assert.AreEqual(0, command.Builder.ExecutionHook.ThreadStart);
            Assert.AreEqual(0, command.Builder.ExecutionHook.ThreadComplete);
            Hystrix.Reset();
        }
Exemplo n.º 2
0
        public void Command_ExecutionSemaphoreWithExecution()
        {
            TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
            // single thread should work
            try
            {
                TestSemaphoreCommand command = new TestSemaphoreCommand(circuitBreaker, 1, TimeSpan.FromMilliseconds(200));
                bool result = command.Execute();
                Assert.IsFalse(command.IsExecutedInThread);
                Assert.IsTrue(result);
            }
            catch (Exception e)
            {
                // we shouldn't fail on this one
                throw new Exception("Unexpected exception.", e);
            }

            ArrayBlockingQueue<bool> results = new ArrayBlockingQueue<bool>(2);

            AtomicBoolean exceptionReceived = new AtomicBoolean();

            TryableSemaphore semaphore =
                new TryableSemaphore(HystrixPropertyFactory.AsProperty(1));

            IRunnable r = new HystrixContextRunnable(new Runnable(() =>
            {
                try
                {
                    results.Add(new TestSemaphoreCommand(circuitBreaker, semaphore, TimeSpan.FromMilliseconds(200)).Execute());
                }
                catch (Exception)
                {

                    exceptionReceived.Value = true;
                }
            }));

            // 2 threads, the second should be rejected by the semaphore
            Thread t1 = new Thread(r.Run);
            Thread t2 = new Thread(r.Run);

            t1.Start();
            t2.Start();
            try
            {
                t1.Join();
                t2.Join();
            }
            catch (Exception)
            {

                Assert.Fail("failed waiting on threads");
            }

            if (!exceptionReceived.Value)
            {
                Assert.Fail("We expected an exception on the 2nd get");
            }

            // only 1 value is expected as the other should have thrown an exception
            Assert.AreEqual(1, results.Count);
            // should contain only a true result
            Assert.IsTrue(results.Contains(true));
            Assert.IsFalse(results.Contains(false));

            Assert.AreEqual(2, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Success));
            Assert.AreEqual(1, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ExceptionThrown));
            // no failure ... we throw an exception because of rejection but the command does not fail execution
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Failure));
            // there is no fallback implemented so no failure can occur on it
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackFailure));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackRejection));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackSuccess));
            // we rejected via semaphore
            Assert.AreEqual(1, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.SemaphoreRejected));
            // the rest should not be involved in this test
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ShortCircuited));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ThreadPoolRejected));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Timeout));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ResponseFromCache));

            Assert.AreEqual(3, HystrixRequestLog.GetCurrentRequest().ExecutedCommands.Count());
            Hystrix.Reset();
        }
Exemplo n.º 3
0
        public void Command_SemaphorePermitsInUse()
        {
            TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();

            // this semaphore will be shared across multiple command instances
            TryableSemaphore sharedSemaphore =
                new TryableSemaphore(HystrixPropertyFactory.AsProperty(3));

            // used to wait until all commands have started
            CountdownEvent startLatch = new CountdownEvent(sharedSemaphore.NumberOfPermits.Get() + 1);

            // used to signal that all command can finish
            CountdownEvent sharedLatch = new CountdownEvent(1);

            IRunnable sharedSemaphoreRunnable = new HystrixContextRunnable(new Runnable(() =>
            {
                try
                {
                    new LatchedSemaphoreCommand(circuitBreaker, sharedSemaphore, startLatch, sharedLatch).Execute();
                }
                catch (Exception)
                {

                }
            }));

            // creates group of threads each using command sharing a single semaphore

            // I create extra threads and commands so that I can verify that some of them fail to obtain a semaphore
            int sharedThreadCount = sharedSemaphore.NumberOfPermits.Get() * 2;
            Thread[] sharedSemaphoreThreads = new Thread[sharedThreadCount];
            for (int i = 0; i < sharedThreadCount; i++)
            {
                sharedSemaphoreThreads[i] = new Thread(sharedSemaphoreRunnable.Run);
            }

            // creates thread using isolated semaphore
            TryableSemaphore isolatedSemaphore =
                new TryableSemaphore(HystrixPropertyFactory.AsProperty(1));

            CountdownEvent isolatedLatch = new CountdownEvent(1);

            // tracks failures to obtain semaphores
            AtomicInteger failureCount = new AtomicInteger();

            Thread isolatedThread = new Thread(new HystrixContextRunnable(new Runnable(() =>
            {
                try
                {
                    new LatchedSemaphoreCommand(circuitBreaker, isolatedSemaphore, startLatch, isolatedLatch).Execute();
                }
                catch (Exception)
                {

                    failureCount.IncrementAndGet();
                }
            })).Run);

            // verifies no permits in use before starting threads
            Assert.AreEqual(0, sharedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for shared semaphore");
            Assert.AreEqual(0, isolatedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for isolated semaphore");

            for (int i = 0; i < sharedThreadCount; i++)
            {
                sharedSemaphoreThreads[i].Start();
            }
            isolatedThread.Start();

            // waits until all commands have started
            try
            {
                startLatch.Wait(TimeSpan.FromSeconds(1.0));
            }
            catch (ThreadInterruptedException e)
            {
                throw new Exception("Unexpected exception.", e);
            }

            // verifies that all semaphores are in use
            Assert.AreEqual(sharedSemaphore.NumberOfPermits.Get(), sharedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for shared semaphore");
            Assert.AreEqual(isolatedSemaphore.NumberOfPermits.Get(), isolatedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for isolated semaphore");

            // signals commands to finish
            sharedLatch.Signal();
            isolatedLatch.Signal();

            try
            {
                for (int i = 0; i < sharedThreadCount; i++)
                {
                    sharedSemaphoreThreads[i].Join();
                }
                isolatedThread.Join();
            }
            catch (Exception)
            {

                Assert.Fail("failed waiting on threads");
            }

            // verifies no permits in use after finishing threads
            Assert.AreEqual(0, sharedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for shared semaphore");
            Assert.AreEqual(0, isolatedSemaphore.GetNumberOfPermitsUsed(), "wrong number of permits for isolated semaphore");

            // verifies that some executions failed
            int expectedFailures = sharedSemaphore.GetNumberOfPermitsUsed();
            Assert.AreEqual(expectedFailures, failureCount.Value, "failures expected but did not happen");
            Hystrix.Reset();
        }
Exemplo n.º 4
0
        public void Command_ExecutionSemaphoreWithQueue()
        {
            TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
            // single thread should work
            try
            {
                bool result = new TestSemaphoreCommand(circuitBreaker, 1, TimeSpan.FromMilliseconds(200)).Queue().Get();
                Assert.IsTrue(result);
            }
            catch (Exception e)
            {
                // we shouldn't fail on this one
                throw new Exception("Unexpected exception.", e);
            }

            AtomicBoolean exceptionReceived = new AtomicBoolean();

            TryableSemaphore semaphore =
                new TryableSemaphore(HystrixPropertyFactory.AsProperty(1));

            IRunnable r = new HystrixContextRunnable(new Runnable(() =>
            {
                try
                {
                    new TestSemaphoreCommand(circuitBreaker, semaphore, TimeSpan.FromMilliseconds(200)).Queue().Get();
                }
                catch (Exception)
                {

                    exceptionReceived.Value = true;
                }
            }));
            // 2 threads, the second should be rejected by the semaphore
            Thread t1 = new Thread(r.Run);
            Thread t2 = new Thread(r.Run);

            t1.Start();
            t2.Start();
            try
            {
                t1.Join();
                t2.Join();
            }
            catch (Exception)
            {

                Assert.Fail("failed waiting on threads");
            }

            if (!exceptionReceived.Value)
            {
                Assert.Fail("We expected an exception on the 2nd get");
            }

            Assert.AreEqual(2, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Success));
            // we don't have a fallback so threw an exception when rejected
            Assert.AreEqual(1, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ExceptionThrown));
            // not a failure as the command never executed so can't fail
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Failure));
            // no fallback failure as there isn't a fallback implemented
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackFailure));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackRejection));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.FallbackSuccess));
            // we should have rejected via semaphore
            Assert.AreEqual(1, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.SemaphoreRejected));
            // the rest should not be involved in this test
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ShortCircuited));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ThreadPoolRejected));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.Timeout));
            Assert.AreEqual(0, circuitBreaker.Metrics.GetRollingCount(HystrixRollingNumberEvent.ResponseFromCache));

            Assert.AreEqual(3, HystrixRequestLog.GetCurrentRequest().ExecutedCommands.Count());
            Hystrix.Reset();
        }