blob: 7a980a39effa6ea68443fe46e17ee45dce706ff0 [file] [log] [blame]
/*
* 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.hadoop.fs.s3a;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import org.apache.hadoop.fs.s3a.s3guard.S3Guard;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.s3a.S3ATestUtils.MetricDiff;
import org.apache.hadoop.fs.s3a.s3guard.BulkOperationState;
import org.apache.hadoop.fs.s3a.s3guard.LocalMetadataStore;
import org.apache.hadoop.fs.s3a.s3guard.MetadataStore;
import org.apache.hadoop.fs.s3a.s3guard.NullMetadataStore;
import org.apache.hadoop.fs.s3a.s3guard.PathMetadata;
import org.apache.hadoop.io.IOUtils;
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
/**
* Tests failed writes to metadata store generate the expected
* MetadataPersistenceException.
*/
@RunWith(Parameterized.class)
public class ITestS3AMetadataPersistenceException extends AbstractS3ATestBase {
private static final Logger LOG =
LoggerFactory.getLogger(ITestS3AMetadataPersistenceException.class);
private S3AFileSystem fs;
private IOException ioException;
private final boolean failOnError;
public ITestS3AMetadataPersistenceException(boolean failOnError) {
this.failOnError = failOnError;
}
@Parameterized.Parameters
public static Collection<Object[]> params() {
return Arrays.asList(new Object[][]{
{true},
{false}
});
}
@Override
protected Configuration createConfiguration() {
Configuration conf = super.createConfiguration();
conf.set(Constants.FAIL_ON_METADATA_WRITE_ERROR,
Boolean.toString(failOnError));
// replaced in setup() by IOExceptionMetadataStore
conf.setClass(Constants.S3_METADATA_STORE_IMPL,
NullMetadataStore.class,
MetadataStore.class);
return conf;
}
@Override
public void setup() throws Exception {
super.setup();
S3AFileSystem contractFs = getFileSystem();
fs = (S3AFileSystem) FileSystem.newInstance(
contractFs.getUri(), contractFs.getConf());
ioException = new IOException();
IOExceptionMetadataStore metadataStore =
new IOExceptionMetadataStore(ioException);
metadataStore.initialize(getConfiguration(),
new S3Guard.TtlTimeProvider(getConfiguration()));
fs.setMetadataStore(metadataStore);
}
@Override
public void teardown() throws Exception {
IOUtils.cleanupWithLogger(LOG, fs);
super.teardown();
}
@Test
public void testFailedMetadataUpdate() throws Throwable {
// write a trivial file
Path testFile = path("testFailedMetadataUpdate");
try {
FSDataOutputStream outputStream = fs.create(testFile);
outputStream.write(1);
if (failOnError) {
// close should throw the expected exception
MetadataPersistenceException thrown =
intercept(
MetadataPersistenceException.class,
outputStream::close);
assertEquals("cause didn't match original exception",
ioException, thrown.getCause());
} else {
MetricDiff ignoredCount = new MetricDiff(fs, Statistic.IGNORED_ERRORS);
// close should merely log and increment the statistic
outputStream.close();
ignoredCount.assertDiffEquals("ignored errors", 1);
}
} finally {
// turn off the store and forcibly delete from the raw bucket.
fs.setMetadataStore(new NullMetadataStore());
fs.delete(testFile, false);
}
}
private static class IOExceptionMetadataStore extends LocalMetadataStore {
private final IOException ioException;
private IOExceptionMetadataStore(IOException ioException) {
this.ioException = ioException;
}
@Override
public void put(PathMetadata meta,
final BulkOperationState operationState) throws IOException {
throw ioException;
}
@Override
public void put(final PathMetadata meta) throws IOException {
put(meta, null);
}
}
}