Repository: reef Updated Branches: refs/heads/master 6977d2291 -> 5e9375dcb
[REEF-1436] Validate Service constructor failure => FailedContext Event This addressed the issue by * Adding test that submits a failing Service configuration. * Adding context ID reporting in FailedContext. * Report the correct ParentContext in FailedContext. JIRA: [REEF-1436](https://issues.apache.org/jira/browse/REEF-1436) Pull request: This closes #1046 Project: http://git-wip-us.apache.org/repos/asf/reef/repo Commit: http://git-wip-us.apache.org/repos/asf/reef/commit/5e9375dc Tree: http://git-wip-us.apache.org/repos/asf/reef/tree/5e9375dc Diff: http://git-wip-us.apache.org/repos/asf/reef/diff/5e9375dc Branch: refs/heads/master Commit: 5e9375dcbf2df081cb611f968c9b0fb55ede0d6b Parents: 6977d22 Author: Andrew Chung <[email protected]> Authored: Fri Jun 17 16:05:01 2016 -0700 Committer: Mariia Mykhailova <[email protected]> Committed: Wed Jun 29 12:19:20 2016 -0700 ---------------------------------------------------------------------- .../FailedContextClr2Java.cpp | 6 - .../Context/ContextClientCodeException.cs | 12 - .../Runtime/Evaluator/Context/ContextManager.cs | 18 +- .../Runtime/Evaluator/Context/ContextRuntime.cs | 21 +- .../User/ServiceConstructorExceptionTest.cs | 242 +++++++++++++++++++ .../Org.Apache.REEF.Tests.csproj | 1 + 6 files changed, 264 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Bridge/FailedContextClr2Java.cpp ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Bridge/FailedContextClr2Java.cpp b/lang/cs/Org.Apache.REEF.Bridge/FailedContextClr2Java.cpp index 7efb2b1..115fddc 100644 --- a/lang/cs/Org.Apache.REEF.Bridge/FailedContextClr2Java.cpp +++ b/lang/cs/Org.Apache.REEF.Bridge/FailedContextClr2Java.cpp @@ -34,18 +34,12 @@ namespace Org { if (env->GetJavaVM(pJavaVm) != 0) { ManagedLog::LOGGER->LogError("Failed to get JavaVM", nullptr); } - ManagedLog::LOGGER->LogStart("FailedContextClr2Java::FailedContextClr2Java"); _jobjectFailedContext = reinterpret_cast<jobject>(env->NewGlobalRef(jobjectFailedContext)); jclass jclassFailedContext = env->GetObjectClass(_jobjectFailedContext); - ManagedLog::LOGGER->LogStart("FailedContextClr2Java::FailedContextClr2Java"); jmethodID jmidGetParentId = env->GetMethodID(jclassFailedContext, "getParentIdString", "()Ljava/lang/String;"); - - ManagedLog::LOGGER->LogStart("FailedContextClr2Java::FailedContextClr2Java"); _jstringContextId = CommonUtilities::GetJObjectId(env, jobjectFailedContext, jclassFailedContext); - ManagedLog::LOGGER->LogStart("FailedContextClr2Java::FailedContextClr2Java"); _jstringEvaluatorId = CommonUtilities::GetJObjectEvaluatorId(env, jobjectFailedContext, jclassFailedContext); - ManagedLog::LOGGER->LogStart("FailedContextClr2Java::FailedContextClr2Java"); _jstringParentContextId = reinterpret_cast<jstring>(env->NewGlobalRef(env->CallObjectMethod(_jobjectFailedContext, jmidGetParentId))); ManagedLog::LOGGER->LogStop("FailedContextClr2Java::FailedContextClr2Java"); http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextClientCodeException.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextClientCodeException.cs b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextClientCodeException.cs index ae8f44b..a6f9b74 100644 --- a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextClientCodeException.cs +++ b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextClientCodeException.cs @@ -16,7 +16,6 @@ // under the License. using System; -using Org.Apache.REEF.Tang.Interface; using Org.Apache.REEF.Utilities; namespace Org.Apache.REEF.Common.Runtime.Evaluator.Context @@ -53,16 +52,5 @@ namespace Org.Apache.REEF.Common.Runtime.Evaluator.Context { get { return _parentId; } } - - /// <summary> - /// Extracts a context id from the given configuration. - /// </summary> - /// <param name="c"></param> - /// <returns>the context id in the given configuration.</returns> - public static string GetId(IConfiguration c) - { - // TODO: update after TANG is available - return string.Empty; - } } } http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextManager.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextManager.cs b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextManager.cs index 1387d51..7f9e846 100644 --- a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextManager.cs +++ b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextManager.cs @@ -184,18 +184,14 @@ namespace Org.Apache.REEF.Common.Runtime.Evaluator.Context Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); } } - catch (Exception e) + catch (TaskClientCodeException e) { - if (e is TaskClientCodeException) - { - HandleTaskException(e as TaskClientCodeException); - } - else if (e is ContextClientCodeException) - { - HandleContextException(e as ContextClientCodeException); - } - Utilities.Diagnostics.Exceptions.CaughtAndThrow(e, Level.Error, LOGGER); - } + HandleTaskException(e); + } + catch (ContextClientCodeException e) + { + HandleContextException(e); + } } /// <summary> http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextRuntime.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextRuntime.cs b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextRuntime.cs index 3a49f53..eced5e2 100644 --- a/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextRuntime.cs +++ b/lang/cs/Org.Apache.REEF.Common/Runtime/Evaluator/Context/ContextRuntime.cs @@ -28,6 +28,7 @@ using Org.Apache.REEF.Common.Services; using Org.Apache.REEF.Common.Tasks; using Org.Apache.REEF.Common.Tasks.Events; using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Implementations.Tang; using Org.Apache.REEF.Tang.Interface; using Org.Apache.REEF.Utilities; using Org.Apache.REEF.Utilities.Attributes; @@ -225,15 +226,21 @@ namespace Org.Apache.REEF.Common.Runtime.Evaluator.Context { Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); - Optional<string> parentId = ParentContext.IsPresent() ? - Optional<string>.Of(ParentContext.Value.Id) : - Optional<string>.Empty(); - ContextClientCodeException ex = new ContextClientCodeException(ContextClientCodeException.GetId(childContextConfiguration), parentId, "Unable to spawn context", e); - - Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + var childContextId = string.Empty; + try + { + var injector = TangFactory.GetTang().NewInjector(childContextConfiguration); + childContextId = injector.GetNamedInstance<ContextConfigurationOptions.ContextIdentifier, string>(); + } + catch (InjectionException) + { + Utilities.Diagnostics.Exceptions.Caught( + e, Level.Error, "Unable to get Context ID from child ContextConfiguration. Using empty string.", LOGGER); + } + + throw new ContextClientCodeException(childContextId, Optional<string>.Of(Id), "Unable to spawn context", e); } } - return null; } /// <summary> http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Tests/Functional/Failure/User/ServiceConstructorExceptionTest.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tests/Functional/Failure/User/ServiceConstructorExceptionTest.cs b/lang/cs/Org.Apache.REEF.Tests/Functional/Failure/User/ServiceConstructorExceptionTest.cs new file mode 100644 index 0000000..3bfecee --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tests/Functional/Failure/User/ServiceConstructorExceptionTest.cs @@ -0,0 +1,242 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using Org.Apache.REEF.Common.Context; +using Org.Apache.REEF.Common.Services; +using Org.Apache.REEF.Common.Tasks; +using Org.Apache.REEF.Driver; +using Org.Apache.REEF.Driver.Context; +using Org.Apache.REEF.Driver.Evaluator; +using Org.Apache.REEF.Driver.Task; +using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Tang.Util; +using Org.Apache.REEF.Tests.Functional.Common.Task; +using Org.Apache.REEF.Utilities.Logging; +using Xunit; + +namespace Org.Apache.REEF.Tests.Functional.Failure.User +{ + [Collection("FunctionalTests")] + public sealed class ServiceConstructorExceptionTest : ReefFunctionalTest + { + private static readonly Logger Logger = Logger.GetLogger(typeof(ServiceConstructorExceptionTest)); + private static readonly string FailedEvaluatorReceived = "FailedEvaluatorReceived"; + private static readonly string ActiveContextReceived = "ActiveContextReceived"; + private static readonly string FailedContextReceived = "FailedContextReceived"; + private static readonly string RunningTaskReceived = "RunningTaskReceived"; + private static readonly string CompletedTaskReceived = "CompletedTaskReceived"; + private static readonly string Context0 = "Context0"; + private static readonly string Context1 = "Context1"; + private static readonly string Context2 = "Context2"; + private static readonly string TaskId = "TaskId"; + private static readonly string TaskRunningMessage = "TaskRunningMessage"; + + /// <summary> + /// This test tests that an Exception in the constructor of a Service object triggers a FailedContext event + /// on a non-root Context. Also tests that an Exception in the constructor of a Service object triggers a FailedEvaluator + /// event on a root Context. Upon failing the non-root context, we submit a Task to the failed context's parent + /// to verify that a Task can still be run on the parent context. + /// </summary> + [Fact] + [Trait("Priority", "1")] + [Trait("Category", "FunctionalGated")] + [Trait("Description", + "Test Exception in the constructor of a Service object and validate that we receive either a FailedContext event " + + "in the case of non-root contexts, or a FailedEvaluator event in the case of the root context.")] + public void TestServiceConstructorExceptionOnLocalRuntime() + { + string testFolder = DefaultRuntimeFolder + TestId; + TestRun( + DriverConfiguration.ConfigurationModule + .Set(DriverConfiguration.OnDriverStarted, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnEvaluatorFailed, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnEvaluatorAllocated, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnContextActive, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnContextFailed, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnTaskRunning, GenericType<ServiceConstructorExceptionDriver>.Class) + .Set(DriverConfiguration.OnTaskCompleted, GenericType<ServiceConstructorExceptionDriver>.Class) + .Build(), + typeof(ServiceConstructorExceptionDriver), 1, "serviceConstructorExceptionTest", "local", testFolder); + + ValidateSuccessForLocalRuntime(numberOfContextsToClose: 1, numberOfTasksToFail: 0, numberOfEvaluatorsToFail: 1, testFolder: testFolder); + ValidateMessagesSuccessfullyLoggedForDriver( + new[] { FailedEvaluatorReceived, ActiveContextReceived, RunningTaskReceived, CompletedTaskReceived }, testFolder, 1); + CleanUp(testFolder); + } + + private sealed class ServiceConstructorExceptionDriver : + IObserver<IDriverStarted>, + IObserver<IAllocatedEvaluator>, + IObserver<IActiveContext>, + IObserver<IFailedContext>, + IObserver<IFailedEvaluator>, + IObserver<IRunningTask>, + IObserver<ICompletedTask> + { + private readonly object _lock = new object(); + private readonly IEvaluatorRequestor _requestor; + private bool _shouldFailOnRootContext = true; + + [Inject] + private ServiceConstructorExceptionDriver(IEvaluatorRequestor requestor) + { + _requestor = requestor; + } + + public void OnNext(IDriverStarted value) + { + _requestor.Submit(_requestor.NewBuilder().SetNumber(2).Build()); + } + + public void OnNext(IFailedEvaluator value) + { + // We should expect 0 failed contexts here, since the Evaluator fails + // to instantiate the RootContext. + Assert.Equal(0, value.FailedContexts.Count); + Logger.Log(Level.Info, FailedEvaluatorReceived); + } + + /// <summary> + /// Submits two contexts, one that fails the Evaluator and another that allows for + /// Context Stacking. + /// </summary> + public void OnNext(IAllocatedEvaluator value) + { + lock (_lock) + { + if (_shouldFailOnRootContext) + { + // Failing config. + var ctxConf = ContextConfiguration.ConfigurationModule + .Set(ContextConfiguration.Identifier, Context0) + .Build(); + + var serviceConf = ServiceConfiguration.ConfigurationModule + .Set(ServiceConfiguration.Services, GenericType<ServiceConstructorExceptionService>.Class) + .Build(); + + value.SubmitContextAndService(ctxConf, serviceConf); + _shouldFailOnRootContext = false; + } + else + { + // Context stacking config. + value.SubmitContext( + ContextConfiguration.ConfigurationModule + .Set(ContextConfiguration.Identifier, Context1) + .Build()); + } + } + } + + /// <summary> + /// Submits the failing Context config on the non-failing Context. + /// </summary> + public void OnNext(IActiveContext value) + { + lock (_lock) + { + Logger.Log(Level.Info, ActiveContextReceived); + Assert.False(_shouldFailOnRootContext); + + var ctxConf = ContextConfiguration.ConfigurationModule + .Set(ContextConfiguration.Identifier, Context2) + .Build(); + + var serviceConf = ServiceConfiguration.ConfigurationModule + .Set(ServiceConfiguration.Services, GenericType<ServiceConstructorExceptionService>.Class) + .Build(); + + value.SubmitContextAndService(ctxConf, serviceConf); + } + } + + /// <summary> + /// Only Context2 is expected as the FailedContext. + /// Context0 should trigger a FailedEvaluator directly. + /// Submit a Task on to the parent of Context2 (Context1) and make sure + /// that it runs to completion. + /// </summary> + public void OnNext(IFailedContext value) + { + Assert.Equal(Context2, value.Id); + Assert.True(value.ParentContext.IsPresent(), "Expected " + Context2 + " to have parent of " + Context1); + + if (value.ParentContext.IsPresent()) + { + // Submit Task on the good Context, i.e. the FailedContext's parent. + Assert.Equal(Context1, value.ParentContext.Value.Id); + value.ParentContext.Value.SubmitTask( + TaskConfiguration.ConfigurationModule + .Set(TaskConfiguration.Identifier, TaskId) + .Set(TaskConfiguration.Task, GenericType<ServiceConstructorExceptionTestTask>.Class) + .Build()); + } + + Logger.Log(Level.Info, FailedContextReceived); + } + + public void OnNext(IRunningTask value) + { + Assert.Equal(TaskId, value.Id); + Assert.Equal(Context1, value.ActiveContext.Id); + Logger.Log(Level.Info, RunningTaskReceived); + } + + public void OnNext(ICompletedTask value) + { + Assert.Equal(TaskId, value.Id); + Logger.Log(Level.Info, CompletedTaskReceived); + value.ActiveContext.Dispose(); + } + + public void OnError(Exception error) + { + } + + public void OnCompleted() + { + } + } + + /// <summary> + /// A test service class that is not injectable. + /// </summary> + private sealed class ServiceConstructorExceptionService + { + /// <summary> + /// Not injectable on purpose to make Service injection fail. + /// </summary> + private ServiceConstructorExceptionService() + { + } + } + + /// <summary> + /// A Test Task class that simply logs a Task running message. + /// </summary> + private sealed class ServiceConstructorExceptionTestTask : LoggingTask + { + [Inject] + private ServiceConstructorExceptionTestTask() + : base(TaskRunningMessage) + { + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/5e9375dc/lang/cs/Org.Apache.REEF.Tests/Org.Apache.REEF.Tests.csproj ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tests/Org.Apache.REEF.Tests.csproj b/lang/cs/Org.Apache.REEF.Tests/Org.Apache.REEF.Tests.csproj index e58f980..c7f295a 100644 --- a/lang/cs/Org.Apache.REEF.Tests/Org.Apache.REEF.Tests.csproj +++ b/lang/cs/Org.Apache.REEF.Tests/Org.Apache.REEF.Tests.csproj @@ -80,6 +80,7 @@ under the License. <Compile Include="Functional\Bridge\TestContextStack.cs" /> <Compile Include="Functional\Bridge\TestFailedEvaluatorEventHandler.cs" /> <Compile Include="Functional\Common\Task\ExceptionTask.cs" /> + <Compile Include="Functional\Failure\User\ServiceConstructorExceptionTest.cs" /> <Compile Include="Functional\Failure\User\TaskCallExceptionTest.cs" /> <Compile Include="Functional\Bridge\Exceptions\TestNonSerializableException.cs" /> <Compile Include="Functional\Bridge\Exceptions\TestSerializableException.cs" />
