/** * 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 org.apache.falcon.state.service.store; import java.util.Map; import org.apache.commons.lang.RandomStringUtils; import org.apache.falcon.FalconException; import org.apache.falcon.cluster.util.EmbeddedCluster; import org.apache.falcon.entity.v0.Entity; import org.apache.falcon.entity.v0.EntityType; import org.apache.falcon.exception.StateStoreException; import org.apache.falcon.execution.ExecutionInstance; import org.apache.falcon.execution.FalconExecutionService; import org.apache.falcon.execution.MockDAGEngine; import org.apache.falcon.execution.NotificationHandler; import org.apache.falcon.notification.service.impl.AlarmService; import org.apache.falcon.notification.service.impl.DataAvailabilityService; import org.apache.falcon.notification.service.impl.JobCompletionService; import org.apache.falcon.notification.service.impl.SchedulerService; import org.apache.falcon.predicate.Predicate; import org.apache.falcon.service.Services; import org.apache.falcon.state.AbstractSchedulerTestBase; import org.apache.falcon.state.EntityClusterID; import org.apache.falcon.state.EntityID; import org.apache.falcon.state.EntityState; import org.apache.falcon.state.ID; import org.apache.falcon.state.InstanceID; import org.apache.falcon.state.InstanceState; import org.apache.falcon.state.store.jdbc.BeanMapperUtil; import org.apache.falcon.state.store.jdbc.JDBCStateStore; import org.apache.falcon.state.store.StateStore; import org.apache.falcon.service.FalconJPAService; import org.apache.falcon.util.StartupProperties; import org.apache.falcon.workflow.engine.DAGEngine; import org.apache.falcon.workflow.engine.DAGEngineFactory; import org.apache.falcon.workflow.engine.OozieDAGEngine; import org.joda.time.DateTime; import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Random; /** * Test cases for JDBCStateStore. */ public class TestJDBCStateStore extends AbstractSchedulerTestBase { private static StateStore stateStore = JDBCStateStore.get(); private static Random randomValGenerator = new Random(); private static FalconJPAService falconJPAService = FalconJPAService.get(); private AlarmService mockTimeService; private DataAvailabilityService mockDataService; private SchedulerService mockSchedulerService; private JobCompletionService mockCompletionService; private DAGEngine dagEngine; @BeforeClass public void setup() throws Exception { super.setup(); createDB(DB_SQL_FILE); falconJPAService.init(); this.dfsCluster = EmbeddedCluster.newCluster("testCluster"); this.conf = dfsCluster.getConf(); registerServices(); } private void registerServices() throws FalconException { mockTimeService = Mockito.mock(AlarmService.class); Mockito.when(mockTimeService.getName()).thenReturn("AlarmService"); Mockito.when(mockTimeService.createRequestBuilder(Mockito.any(NotificationHandler.class), Mockito.any(ID.class))).thenCallRealMethod(); mockDataService = Mockito.mock(DataAvailabilityService.class); Mockito.when(mockDataService.getName()).thenReturn("DataAvailabilityService"); Mockito.when(mockDataService.createRequestBuilder(Mockito.any(NotificationHandler.class), Mockito.any(ID.class))).thenCallRealMethod(); dagEngine = Mockito.mock(OozieDAGEngine.class); Mockito.doNothing().when(dagEngine).resume(Mockito.any(ExecutionInstance.class)); mockSchedulerService = Mockito.mock(SchedulerService.class); Mockito.when(mockSchedulerService.getName()).thenReturn("JobSchedulerService"); StartupProperties.get().setProperty("dag.engine.impl", MockDAGEngine.class.getName()); StartupProperties.get().setProperty("execution.service.impl", FalconExecutionService.class.getName()); dagEngine = Mockito.spy(DAGEngineFactory.getDAGEngine("testCluster")); Mockito.when(mockSchedulerService.createRequestBuilder(Mockito.any(NotificationHandler.class), Mockito.any(ID.class))).thenCallRealMethod(); mockCompletionService = Mockito.mock(JobCompletionService.class); Mockito.when(mockCompletionService.getName()).thenReturn("JobCompletionService"); Mockito.when(mockCompletionService.createRequestBuilder(Mockito.any(NotificationHandler.class), Mockito.any(ID.class))).thenCallRealMethod(); Services.get().register(mockTimeService); Services.get().register(mockDataService); Services.get().register(mockSchedulerService); Services.get().register(mockCompletionService); } @Test public void testInsertRetrieveAndUpdate() throws Exception { EntityState entityState = getEntityState(EntityType.PROCESS, "process"); stateStore.putEntity(entityState); EntityID entityID = new EntityID(entityState.getEntity()); EntityState actualEntityState = stateStore.getEntity(entityID); Assert.assertEquals(actualEntityState.getEntity(), entityState.getEntity()); Assert.assertEquals(actualEntityState.getCurrentState(), entityState.getCurrentState()); try { stateStore.putEntity(entityState); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { //no op } entityState.setCurrentState(EntityState.STATE.SCHEDULED); stateStore.updateEntity(entityState); actualEntityState = stateStore.getEntity(entityID); Assert.assertEquals(actualEntityState.getEntity(), entityState.getEntity()); Assert.assertEquals(actualEntityState.getCurrentState(), entityState.getCurrentState()); stateStore.deleteEntity(entityID); boolean entityExists = stateStore.entityExists(entityID); Assert.assertEquals(entityExists, false); try { stateStore.getEntity(entityID); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e){ // no op } try { stateStore.updateEntity(entityState); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { // no op } try { stateStore.deleteEntity(entityID); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e){ // no op } } @Test public void testGetEntities() throws Exception { EntityState entityState1 = getEntityState(EntityType.PROCESS, "process1"); EntityState entityState2 = getEntityState(EntityType.PROCESS, "process2"); EntityState entityState3 = getEntityState(EntityType.FEED, "feed1"); Collection<EntityState> result = stateStore.getAllEntities(); Assert.assertEquals(result.size(), 0); stateStore.putEntity(entityState1); stateStore.putEntity(entityState2); stateStore.putEntity(entityState3); result = stateStore.getAllEntities(); Assert.assertEquals(result.size(), 3); Collection<Entity> entities = stateStore.getEntities(EntityState.STATE.SUBMITTED); Assert.assertEquals(entities.size(), 3); } @Test public void testInstanceInsertionAndUpdate() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); EntityState entityState = getEntityState(EntityType.PROCESS, "process"); ExecutionInstance executionInstance = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis(), "cluster", System.currentTimeMillis()); InstanceState instanceState = new InstanceState(executionInstance); initInstanceState(instanceState); stateStore.putExecutionInstance(instanceState); InstanceID instanceID = new InstanceID(instanceState.getInstance()); InstanceState actualInstanceState = stateStore.getExecutionInstance(instanceID); Assert.assertEquals(actualInstanceState, instanceState); instanceState.setCurrentState(InstanceState.STATE.RUNNING); Predicate predicate = new Predicate(Predicate.TYPE.DATA); instanceState.getInstance().getAwaitingPredicates().add(predicate); stateStore.updateExecutionInstance(instanceState); actualInstanceState = stateStore.getExecutionInstance(instanceID); Assert.assertEquals(actualInstanceState, instanceState); try { stateStore.putExecutionInstance(instanceState); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { // no op } stateStore.deleteExecutionInstance(instanceID); try { stateStore.getExecutionInstance(instanceID); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { // no op } try { stateStore.deleteExecutionInstance(instanceID); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { // no op } try { stateStore.updateExecutionInstance(instanceState); Assert.fail("Exception must have been thrown"); } catch (StateStoreException e) { // no op } } @Test public void testBulkInstanceOperations() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); EntityState entityState = getEntityState(EntityType.PROCESS, "process1"); ExecutionInstance processExecutionInstance1 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis() - 60000, "cluster1", System.currentTimeMillis() - 60000); InstanceState instanceState1 = new InstanceState(processExecutionInstance1); instanceState1.setCurrentState(InstanceState.STATE.READY); ExecutionInstance processExecutionInstance2 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis(), "cluster1", System.currentTimeMillis()); InstanceState instanceState2 = new InstanceState(processExecutionInstance2); instanceState2.setCurrentState(InstanceState.STATE.RUNNING); ExecutionInstance processExecutionInstance3 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis(), "cluster2", System.currentTimeMillis()); InstanceState instanceState3 = new InstanceState(processExecutionInstance3); instanceState3.setCurrentState(InstanceState.STATE.READY); stateStore.putExecutionInstance(instanceState1); stateStore.putExecutionInstance(instanceState2); stateStore.putExecutionInstance(instanceState3); Collection<InstanceState> actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster1"); Assert.assertEquals(actualInstances.size(), 2); Assert.assertEquals(actualInstances.toArray()[0], instanceState1); Assert.assertEquals(actualInstances.toArray()[1], instanceState2); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster2"); Assert.assertEquals(actualInstances.size(), 1); Assert.assertEquals(actualInstances.toArray()[0], instanceState3); List<InstanceState.STATE> states = new ArrayList<>(); states.add(InstanceState.STATE.READY); actualInstances = stateStore.getExecutionInstances(entityState.getEntity(), "cluster1", states); Assert.assertEquals(actualInstances.size(), 1); Assert.assertEquals(actualInstances.toArray()[0], instanceState1); EntityClusterID entityClusterID = new EntityClusterID(entityState.getEntity(), "testCluster"); actualInstances = stateStore.getExecutionInstances(entityClusterID, states); Assert.assertEquals(actualInstances.size(), 2); Assert.assertEquals(actualInstances.toArray()[0], instanceState1); Assert.assertEquals(actualInstances.toArray()[1], instanceState3); states.add(InstanceState.STATE.RUNNING); actualInstances = stateStore.getExecutionInstances(entityState.getEntity(), "cluster1", states); Assert.assertEquals(actualInstances.size(), 2); Assert.assertEquals(actualInstances.toArray()[0], instanceState1); Assert.assertEquals(actualInstances.toArray()[1], instanceState2); InstanceState lastInstanceState = stateStore.getLastExecutionInstance(entityState.getEntity(), "cluster1"); Assert.assertEquals(lastInstanceState, instanceState2); InstanceID instanceKey = new InstanceID(instanceState3.getInstance()); stateStore.deleteExecutionInstance(instanceKey); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster2"); Assert.assertEquals(actualInstances.size(), 0); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster1"); Assert.assertEquals(actualInstances.size(), 2); stateStore.putExecutionInstance(instanceState3); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster2"); Assert.assertEquals(actualInstances.size(), 1); stateStore.deleteExecutionInstances(entityClusterID.getEntityID()); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster1"); Assert.assertEquals(actualInstances.size(), 0); actualInstances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster2"); Assert.assertEquals(actualInstances.size(), 0); } @Test public void testGetExecutionInstancesWithRange() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); long instance1Time = System.currentTimeMillis() - 180000; long instance2Time = System.currentTimeMillis(); EntityState entityState = getEntityState(EntityType.PROCESS, "process1"); ExecutionInstance processExecutionInstance1 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance1Time, "cluster1", instance1Time); InstanceState instanceState1 = new InstanceState(processExecutionInstance1); instanceState1.setCurrentState(InstanceState.STATE.RUNNING); ExecutionInstance processExecutionInstance2 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance2Time, "cluster1", instance2Time); InstanceState instanceState2 = new InstanceState(processExecutionInstance2); instanceState2.setCurrentState(InstanceState.STATE.RUNNING); ExecutionInstance processExecutionInstance3 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance2Time, "cluster2", instance2Time); InstanceState instanceState3 = new InstanceState(processExecutionInstance3); instanceState3.setCurrentState(InstanceState.STATE.RUNNING); stateStore.putExecutionInstance(instanceState1); stateStore.putExecutionInstance(instanceState2); stateStore.putExecutionInstance(instanceState3); List<InstanceState.STATE> states = new ArrayList<>(); states.add(InstanceState.STATE.RUNNING); Collection<InstanceState> actualInstances = stateStore.getExecutionInstances(entityState.getEntity(), "cluster1", states, new DateTime(instance1Time), new DateTime(instance1Time + 60000)); Assert.assertEquals(actualInstances.size(), 1); Assert.assertEquals(actualInstances.toArray()[0], instanceState1); actualInstances = stateStore.getExecutionInstances(entityState.getEntity(), "cluster1", states, new DateTime(instance2Time), new DateTime(instance2Time + 60000)); Assert.assertEquals(actualInstances.size(), 1); Assert.assertEquals(actualInstances.toArray()[0], instanceState2); // Ensure we can get instances for a different cluster actualInstances = stateStore.getExecutionInstances(entityState.getEntity(), "cluster2", states, new DateTime(instance2Time), new DateTime(instance2Time + 60000)); Assert.assertEquals(actualInstances.size(), 1); Assert.assertEquals(actualInstances.toArray()[0], instanceState3); } @Test public void testGetInstanceFromExternalID() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); long instance1Time = System.currentTimeMillis() - 180000; long instance2Time = System.currentTimeMillis(); EntityState entityState = getEntityState(EntityType.PROCESS, "processext"); ExecutionInstance processExecutionInstance1 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance1Time, "cluster1", instance1Time); processExecutionInstance1.setExternalID("external_id_1"); InstanceState instanceState1 = new InstanceState(processExecutionInstance1); instanceState1.setCurrentState(InstanceState.STATE.RUNNING); ExecutionInstance processExecutionInstance2 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance2Time, "cluster1", instance2Time); processExecutionInstance2.setExternalID("external_id_2"); InstanceState instanceState2 = new InstanceState(processExecutionInstance2); instanceState2.setCurrentState(InstanceState.STATE.RUNNING); stateStore.putExecutionInstance(instanceState1); stateStore.putExecutionInstance(instanceState2); InstanceState actualInstanceState = stateStore.getExecutionInstance("external_id_1"); Assert.assertEquals(actualInstanceState.getInstance(), processExecutionInstance1); actualInstanceState = stateStore.getExecutionInstance("external_id_2"); Assert.assertEquals(actualInstanceState.getInstance(), processExecutionInstance2); } @Test public void testCascadingDelete() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); EntityState entityState = getEntityState(EntityType.PROCESS, "process1"); stateStore.putEntity(entityState); ExecutionInstance processExecutionInstance1 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis() - 60000, "cluster1", System.currentTimeMillis() - 60000); InstanceState instanceState1 = new InstanceState(processExecutionInstance1); instanceState1.setCurrentState(InstanceState.STATE.READY); ExecutionInstance processExecutionInstance2 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), System.currentTimeMillis(), "cluster1", System.currentTimeMillis()); InstanceState instanceState2 = new InstanceState(processExecutionInstance2); instanceState2.setCurrentState(InstanceState.STATE.RUNNING); stateStore.putExecutionInstance(instanceState1); stateStore.putExecutionInstance(instanceState2); Collection<InstanceState> instances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster1"); Assert.assertEquals(instances.size(), 2); stateStore.deleteEntity(new EntityID(entityState.getEntity())); deleteEntity(EntityType.PROCESS, "process1"); instances = stateStore.getAllExecutionInstances(entityState.getEntity(), "cluster1"); Assert.assertEquals(instances.size(), 0); } @Test public void testGetExecutionSummaryWithRange() throws Exception { storeEntity(EntityType.CLUSTER, "testCluster"); storeEntity(EntityType.FEED, "clicksFeed"); storeEntity(EntityType.FEED, "clicksSummary"); long instance1Time = System.currentTimeMillis() - 180000; long instance2Time = System.currentTimeMillis(); EntityState entityState = getEntityState(EntityType.PROCESS, "clicksProcess"); ExecutionInstance processExecutionInstance1 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance1Time, "cluster1", instance1Time); InstanceState instanceState1 = new InstanceState(processExecutionInstance1); instanceState1.setCurrentState(InstanceState.STATE.RUNNING); ExecutionInstance processExecutionInstance2 = BeanMapperUtil.getExecutionInstance( entityState.getEntity().getEntityType(), entityState.getEntity(), instance2Time, "cluster1", instance2Time); InstanceState instanceState2 = new InstanceState(processExecutionInstance2); instanceState2.setCurrentState(InstanceState.STATE.SUCCEEDED); stateStore.putExecutionInstance(instanceState1); stateStore.putExecutionInstance(instanceState2); Map<InstanceState.STATE, Long> summary = stateStore.getExecutionInstanceSummary(entityState.getEntity(), "cluster1", new DateTime(instance1Time), new DateTime(instance1Time + 60000)); Assert.assertEquals(summary.size(), 1); Assert.assertEquals(summary.get(InstanceState.STATE.RUNNING).longValue(), 1L); summary = stateStore.getExecutionInstanceSummary(entityState.getEntity(), "cluster1", new DateTime(instance2Time), new DateTime(instance2Time + 60000)); Assert.assertEquals(summary.size(), 1); Assert.assertEquals(summary.get(InstanceState.STATE.SUCCEEDED).longValue(), 1L); } private void initInstanceState(InstanceState instanceState) { instanceState.setCurrentState(InstanceState.STATE.READY); instanceState.getInstance().setExternalID(RandomStringUtils.randomNumeric(6)); instanceState.getInstance().setInstanceSequence(randomValGenerator.nextInt()); instanceState.getInstance().setActualStart(new DateTime(System.currentTimeMillis())); instanceState.getInstance().setActualEnd(new DateTime(System.currentTimeMillis())); List<Predicate> predicates = new ArrayList<>(); Predicate predicate = new Predicate(Predicate.TYPE.JOB_COMPLETION); predicates.add(predicate); instanceState.getInstance().setAwaitingPredicates(predicates); } private EntityState getEntityState(EntityType entityType, String name) throws Exception { storeEntity(entityType, name); Entity entity = getStore().get(entityType, name); Assert.assertNotNull(entity); return new EntityState(entity); } @AfterTest public void cleanUpTables() throws StateStoreException { try { stateStore.deleteEntities(); } catch (Exception e) { // ignore } } @AfterClass public void cleanup() throws IOException { super.cleanup(); } }