/* * 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.flink.runtime.state; import org.apache.flink.configuration.Configuration; import org.apache.flink.configuration.CoreOptions; import org.apache.flink.configuration.IllegalConfigurationException; import org.apache.flink.core.fs.Path; import org.apache.flink.runtime.state.filesystem.FsStateBackend; import org.apache.flink.runtime.state.filesystem.FsStateBackendFactory; import org.apache.flink.runtime.state.memory.MemoryStateBackend; import org.apache.flink.util.DynamicCodeLoadingException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * This test validates that state backends are properly loaded from configuration. */ public class StateBackendLoadingTest { @Rule public final TemporaryFolder tmp = new TemporaryFolder(); private final ClassLoader cl = getClass().getClassLoader(); private final String backendKey = CoreOptions.STATE_BACKEND.key(); // ------------------------------------------------------------------------ @Test public void testNoStateBackendDefined() throws Exception { assertNull(AbstractStateBackend.loadStateBackendFromConfig(new Configuration(), cl, null)); } @Test public void testInstantiateMemoryBackendByDefault() throws Exception { StateBackend backend = AbstractStateBackend .loadStateBackendFromConfigOrCreateDefault(new Configuration(), cl, null); assertTrue(backend instanceof MemoryStateBackend); } @Test public void testLoadMemoryStateBackend() throws Exception { // we configure with the explicit string (rather than AbstractStateBackend#X_STATE_BACKEND_NAME) // to guard against config-breaking changes of the name final Configuration config = new Configuration(); config.setString(backendKey, "jobmanager"); StateBackend backend = AbstractStateBackend .loadStateBackendFromConfigOrCreateDefault(new Configuration(), cl, null); assertTrue(backend instanceof MemoryStateBackend); } @Test public void testLoadFileSystemStateBackend() throws Exception { final String checkpointDir = new Path(tmp.getRoot().toURI()).toString(); final Path expectedPath = new Path(checkpointDir); final int threshold = 1000000; // we configure with the explicit string (rather than AbstractStateBackend#X_STATE_BACKEND_NAME) // to guard against config-breaking changes of the name final Configuration config1 = new Configuration(); config1.setString(backendKey, "filesystem"); config1.setString("state.checkpoints.dir", checkpointDir); config1.setString("state.backend.fs.checkpointdir", checkpointDir); config1.setInteger("state.backend.fs.memory-threshold", threshold); final Configuration config2 = new Configuration(); config2.setString(backendKey, FsStateBackendFactory.class.getName()); config2.setString("state.checkpoints.dir", checkpointDir); config2.setString("state.backend.fs.checkpointdir", checkpointDir); config2.setInteger("state.backend.fs.memory-threshold", threshold); StateBackend backend1 = AbstractStateBackend .loadStateBackendFromConfigOrCreateDefault(config1, cl, null); StateBackend backend2 = AbstractStateBackend .loadStateBackendFromConfigOrCreateDefault(config2, cl, null); assertTrue(backend1 instanceof FsStateBackend); assertTrue(backend2 instanceof FsStateBackend); FsStateBackend fs1 = (FsStateBackend) backend1; FsStateBackend fs2 = (FsStateBackend) backend2; assertEquals(expectedPath, fs1.getBasePath()); assertEquals(expectedPath, fs2.getBasePath()); assertEquals(threshold, fs1.getMinFileSizeThreshold()); assertEquals(threshold, fs2.getMinFileSizeThreshold()); } /** * This test makes sure that failures properly manifest when the state backend could not be loaded. */ @Test public void testLoadingFails() throws Exception { final Configuration config = new Configuration(); // try a value that is neither recognized as a name, nor corresponds to a class config.setString(backendKey, "does.not.exist"); try { AbstractStateBackend.loadStateBackendFromConfigOrCreateDefault(config, cl, null); fail("should fail with an exception"); } catch (DynamicCodeLoadingException ignored) { // expected } // try a class that is not a factory config.setString(backendKey, java.io.File.class.getName()); try { AbstractStateBackend.loadStateBackendFromConfigOrCreateDefault(config, cl, null); fail("should fail with an exception"); } catch (DynamicCodeLoadingException ignored) { // expected } // a factory that fails config.setString(backendKey, FailingFactory.class.getName()); try { AbstractStateBackend.loadStateBackendFromConfigOrCreateDefault(config, cl, null); fail("should fail with an exception"); } catch (IOException ignored) { // expected } } // ------------------------------------------------------------------------ static final class FailingFactory implements StateBackendFactory<StateBackend> { private static final long serialVersionUID = 1L; @Override public StateBackend createFromConfig(Configuration config) throws IllegalConfigurationException, IOException { throw new IOException("fail!"); } } }