/* * 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. */ package gobblin.runtime; import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.testng.Assert; import org.testng.annotations.Test; import com.google.common.collect.Sets; import com.typesafe.config.ConfigFactory; import gobblin.broker.gobblin_scopes.GobblinScopeTypes; import gobblin.broker.iface.ConfigView; import gobblin.broker.iface.NoSuchScopeException; import gobblin.broker.iface.NotConfiguredException; import gobblin.broker.iface.ScopedConfigView; import gobblin.broker.iface.SharedResourceFactory; import gobblin.broker.iface.SharedResourceKey; import gobblin.broker.iface.SharedResourcesBroker; import gobblin.configuration.WorkUnitState; import gobblin.converter.Converter; import gobblin.converter.DataConversionException; import gobblin.converter.SchemaConversionException; import gobblin.converter.SingleRecordIterable; import gobblin.runtime.api.GobblinInstanceDriver; import gobblin.runtime.api.JobExecutionDriver; import gobblin.runtime.api.JobExecutionResult; import gobblin.runtime.api.JobLifecycleListener; import gobblin.runtime.api.JobSpec; import gobblin.runtime.instance.StandardGobblinInstanceDriver; import gobblin.runtime.instance.StandardGobblinInstanceLauncher; import gobblin.runtime.std.DefaultJobLifecycleListenerImpl; import gobblin.runtime.std.FilteredJobLifecycleListener; import gobblin.runtime.std.JobSpecFilter; import gobblin.writer.test.GobblinTestEventBusWriter; import gobblin.writer.test.TestingEventBusAsserter; import gobblin.writer.test.TestingEventBuses; import gobblin.broker.ResourceInstance; import lombok.Data; import lombok.EqualsAndHashCode; public class JobBrokerInjectionTest { @Test public void testBrokerIsAcquiredAndShared() throws Exception { StandardGobblinInstanceLauncher.Builder instanceLauncherBuilder = StandardGobblinInstanceLauncher.builder() .withInstanceName("testSubmitToJobCatalog"); instanceLauncherBuilder.driver(); StandardGobblinInstanceLauncher instanceLauncher = instanceLauncherBuilder.build(); instanceLauncher.startAsync(); instanceLauncher.awaitRunning(5, TimeUnit.SECONDS); JobSpec js1 = JobSpec.builder() .withConfig(ConfigFactory.parseResources("brokerTest/SimpleHelloWorldJob.jobconf")) .build(); final String eventBusId = js1.getConfig().resolve().getString(GobblinTestEventBusWriter.FULL_EVENTBUSID_KEY); TestingEventBusAsserter asserter = new TestingEventBusAsserter(eventBusId); final StandardGobblinInstanceDriver instance = (StandardGobblinInstanceDriver)instanceLauncher.getDriver(); final ArrayBlockingQueue<JobExecutionDriver> jobDrivers = new ArrayBlockingQueue<>(1); JobLifecycleListener js1Listener = new FilteredJobLifecycleListener( JobSpecFilter.eqJobSpecURI(js1.getUri()), new DefaultJobLifecycleListenerImpl(instance.getLog()) { @Override public void onJobLaunch(JobExecutionDriver jobDriver) { super.onJobLaunch(jobDriver); try { jobDrivers.offer(jobDriver, 5, TimeUnit.SECONDS); } catch (InterruptedException e) { instance.getLog().error("Offer interrupted."); } } }); instance.registerWeakJobLifecycleListener(js1Listener); instance.getMutableJobCatalog().put(js1); JobExecutionDriver jobDriver = jobDrivers.poll(10, TimeUnit.SECONDS); Assert.assertNotNull(jobDriver); JobExecutionResult jobResult = jobDriver.get(100000, TimeUnit.SECONDS); Assert.assertTrue(jobResult.isSuccessful()); Queue<TestingEventBuses.Event> events = asserter.getEvents(); Set<Long> seenInstanceObjectIds = Sets.newHashSet(); Set<Long> seenJobObjectIds = Sets.newHashSet(); Set<Long> seenTaskObjectIds = Sets.newHashSet(); for (TestingEventBuses.Event event : events) { MyRecord record = (MyRecord) event.getValue(); seenInstanceObjectIds.add(record.getInstanceSharedObjectId()); seenJobObjectIds.add(record.getJobSharedObjectId()); seenTaskObjectIds.add(record.getTaskSharedObjectId()); } // Should see same instance and job id (only 1 id in the set), but 5 different task ids for each task Assert.assertEquals(seenInstanceObjectIds.size(), 1); Assert.assertEquals(seenJobObjectIds.size(), 1); Assert.assertEquals(seenTaskObjectIds.size(), 5); asserter.clear(); instance.getMutableJobCatalog().remove(js1.getUri()); instance.getMutableJobCatalog().put(js1); jobDriver = jobDrivers.poll(10, TimeUnit.SECONDS); Assert.assertNotNull(jobDriver); jobResult = jobDriver.get(10, TimeUnit.SECONDS); Assert.assertTrue(jobResult.isSuccessful()); events = asserter.getEvents(); for (TestingEventBuses.Event event : events) { MyRecord record = (MyRecord) event.getValue(); seenInstanceObjectIds.add(record.getInstanceSharedObjectId()); seenJobObjectIds.add(record.getJobSharedObjectId()); seenTaskObjectIds.add(record.getTaskSharedObjectId()); } // A different job should produce a new shared object id Assert.assertEquals(seenInstanceObjectIds.size(), 1); Assert.assertEquals(seenJobObjectIds.size(), 2); Assert.assertEquals(seenTaskObjectIds.size(), 10); } private void launchJob(StandardGobblinInstanceLauncher instanceLauncher, JobSpec js1, GobblinInstanceDriver instance) throws TimeoutException, InterruptedException, ExecutionException { JobExecutionDriver jobDriver = instance.getJobLauncher().launchJob(js1); new Thread(jobDriver).run(); JobExecutionResult jobResult = jobDriver.get(5, TimeUnit.SECONDS); Assert.assertTrue(jobResult.isSuccessful()); } public static class JobBrokerConverter extends Converter<String, String, String, MyRecord> { private MySharedObject instanceSharedObject; private MySharedObject jobSharedObject; private MySharedObject taskSharedObject; @Override public String convertSchema(String inputSchema, WorkUnitState workUnit) throws SchemaConversionException { try { try { this.instanceSharedObject = workUnit.getTaskBroker() .getSharedResourceAtScope(new MySharedObjectFactory(), new MySharedKey(), GobblinScopeTypes.INSTANCE); this.jobSharedObject = workUnit.getTaskBroker() .getSharedResourceAtScope(new MySharedObjectFactory(), new MySharedKey(), GobblinScopeTypes.JOB); this.taskSharedObject = workUnit.getTaskBroker() .getSharedResourceAtScope(new MySharedObjectFactory(), new MySharedKey(), GobblinScopeTypes.TASK); } catch (NoSuchScopeException nsse) { throw new RuntimeException(nsse); } return inputSchema; } catch (NotConfiguredException nce) { throw new RuntimeException(nce); } } @Override public Iterable<MyRecord> convertRecord(String outputSchema, String inputRecord, WorkUnitState workUnit) throws DataConversionException { return new SingleRecordIterable<>(new MyRecord(this.taskSharedObject.id, this.jobSharedObject.id, this.instanceSharedObject.id)); } } @Data public static class MyRecord { private final long taskSharedObjectId; private final long jobSharedObjectId; private final long instanceSharedObjectId; } public static class MySharedObject { private final long id = new Random().nextLong(); } public static class MySharedObjectFactory implements SharedResourceFactory<MySharedObject, MySharedKey, GobblinScopeTypes> { @Override public String getName() { return MySharedObjectFactory.class.getSimpleName(); } @Override public ResourceInstance<MySharedObject> createResource(SharedResourcesBroker<GobblinScopeTypes> broker, ScopedConfigView<GobblinScopeTypes, MySharedKey> config) { return new ResourceInstance<>(new MySharedObject()); } @Override public GobblinScopeTypes getAutoScope(SharedResourcesBroker<GobblinScopeTypes> broker, ConfigView<GobblinScopeTypes, MySharedKey> config) { return broker.selfScope().getType(); } } @EqualsAndHashCode public static class MySharedKey implements SharedResourceKey { @Override public String toConfigurationKey() { return "key"; } } }