|
18 | 18 |
|
19 | 19 | package org.apache.hadoop.hdfs.server.datanode; |
20 | 20 |
|
| 21 | +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ENABLED_OPS_FILEIO_FAULT_INJECTION_KEY; |
| 22 | +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_DEFAULT; |
| 23 | +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_KEY; |
| 24 | +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_FILEIO_FAULT_PERCENTAGE_KEY; |
| 25 | + |
| 26 | +import java.util.HashSet; |
| 27 | +import java.util.Set; |
| 28 | +import java.util.concurrent.ThreadLocalRandom; |
| 29 | + |
| 30 | +import javax.annotation.Nullable; |
21 | 31 |
|
22 | 32 | import org.apache.hadoop.classification.InterfaceAudience; |
| 33 | +import org.apache.hadoop.classification.VisibleForTesting; |
23 | 34 | import org.apache.hadoop.conf.Configuration; |
24 | | -import org.apache.hadoop.hdfs.DFSConfigKeys; |
25 | 35 | import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi; |
26 | | - |
27 | | -import javax.annotation.Nullable; |
| 36 | +import org.slf4j.Logger; |
| 37 | +import org.slf4j.LoggerFactory; |
28 | 38 |
|
29 | 39 | /** |
30 | 40 | * Injects faults in the metadata and data related operations on datanode |
|
33 | 43 | @InterfaceAudience.Private |
34 | 44 | public class FaultInjectorFileIoEvents { |
35 | 45 |
|
| 46 | + public static final class InjectedFileIOFaultException extends Exception { |
| 47 | + |
| 48 | + private static final long serialVersionUID = 1L; |
| 49 | + |
| 50 | + private InjectedFileIOFaultException() { |
| 51 | + super("Fault injected by configuration"); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + public static final Logger LOG = LoggerFactory.getLogger( |
| 56 | + FaultInjectorFileIoEvents.class); |
| 57 | + |
36 | 58 | private final boolean isEnabled; |
| 59 | + private final Set<FileIoProvider.OPERATION> configuredOps; |
| 60 | + private final int faultRangeMax; |
37 | 61 |
|
38 | 62 | public FaultInjectorFileIoEvents(@Nullable Configuration conf) { |
39 | 63 | if (conf != null) { |
40 | | - isEnabled = conf.getBoolean(DFSConfigKeys |
41 | | - .DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_KEY, DFSConfigKeys |
42 | | - .DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_DEFAULT); |
| 64 | + isEnabled = conf.getBoolean( |
| 65 | + DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_KEY, |
| 66 | + DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_DEFAULT); |
43 | 67 | } else { |
44 | 68 | isEnabled = false; |
45 | 69 | } |
| 70 | + configuredOps = new HashSet<>(); |
| 71 | + if (isEnabled) { |
| 72 | + String ops = conf.get( |
| 73 | + DFS_DATANODE_ENABLED_OPS_FILEIO_FAULT_INJECTION_KEY); |
| 74 | + if (ops != null) { |
| 75 | + String[] parts = ops.split(","); |
| 76 | + for (String part : parts) { |
| 77 | + String opName = part.trim().toUpperCase(); |
| 78 | + try { |
| 79 | + configuredOps.add(FileIoProvider.OPERATION.valueOf(opName)); |
| 80 | + } catch (IllegalArgumentException e) { |
| 81 | + LOG.warn("Value '{}' is not valid FileIoProvider.OPERATION, " |
| 82 | + + "ignoring...", opName); |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + int faultPercentagePropVal = Math.min(conf.getInt( |
| 87 | + DFS_DATANODE_FILEIO_FAULT_PERCENTAGE_KEY, 0), 100); |
| 88 | + faultRangeMax = (int) ((double) faultPercentagePropVal / 100 * |
| 89 | + Integer.MAX_VALUE); |
| 90 | + LOG.warn("FaultInjectorFileIoEvents is enabled and will fail the " |
| 91 | + + "following operations: {}", configuredOps); |
| 92 | + LOG.warn(" *** DO NOT USE IN PRODUCTION!!! ***"); |
| 93 | + } else { |
| 94 | + faultRangeMax = 0; |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + @VisibleForTesting |
| 99 | + boolean isEnabled() { |
| 100 | + return isEnabled; |
| 101 | + } |
| 102 | + |
| 103 | + @VisibleForTesting |
| 104 | + Set<FileIoProvider.OPERATION> getOperations() { |
| 105 | + return configuredOps; |
| 106 | + } |
| 107 | + |
| 108 | + @VisibleForTesting |
| 109 | + int getFaultRangeMax() { |
| 110 | + return faultRangeMax; |
| 111 | + } |
| 112 | + |
| 113 | + private void fault(FileIoProvider.OPERATION op) |
| 114 | + throws InjectedFileIOFaultException { |
| 115 | + if (isEnabled && faultRangeMax > 0 && configuredOps.contains(op) |
| 116 | + && ThreadLocalRandom.current().nextInt() < faultRangeMax) { |
| 117 | + LOG.error("Throwing fault for operation: " + op); |
| 118 | + throw new InjectedFileIOFaultException(); |
| 119 | + } |
46 | 120 | } |
47 | 121 |
|
48 | | - public void beforeMetadataOp( |
49 | | - @Nullable FsVolumeSpi volume, FileIoProvider.OPERATION op) { |
| 122 | + public void beforeMetadataOp(@Nullable FsVolumeSpi volume, |
| 123 | + FileIoProvider.OPERATION op) throws InjectedFileIOFaultException { |
| 124 | + fault(op); |
50 | 125 | } |
51 | 126 |
|
52 | | - public void beforeFileIo( |
53 | | - @Nullable FsVolumeSpi volume, FileIoProvider.OPERATION op, long len) { |
| 127 | + public void beforeFileIo(@Nullable FsVolumeSpi volume, |
| 128 | + FileIoProvider.OPERATION op, long len) |
| 129 | + throws InjectedFileIOFaultException { |
| 130 | + fault(op); |
54 | 131 | } |
55 | 132 | } |
0 commit comments