001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.jupiter.api.Assertions.assertThrows;
021
022import java.io.IOException;
023import java.util.List;
024import java.util.Optional;
025import org.apache.hadoop.hbase.Cell;
026import org.apache.hadoop.hbase.DoNotRetryIOException;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.coprocessor.ObserverContext;
031import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
032import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
033import org.apache.hadoop.hbase.coprocessor.RegionObserver;
034import org.apache.hadoop.hbase.testclassification.ClientTests;
035import org.apache.hadoop.hbase.testclassification.MediumTests;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.apache.hadoop.hbase.wal.WALEdit;
038import org.junit.jupiter.api.AfterAll;
039import org.junit.jupiter.api.BeforeAll;
040import org.junit.jupiter.api.Tag;
041import org.junit.jupiter.api.Test;
042
043@Tag(MediumTests.TAG)
044@Tag(ClientTests.TAG)
045public class TestTableOperationException {
046
047  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
048
049  private static TableName TABLE_DONOT_RETRY = TableName.valueOf("TableDoNotRetry");
050
051  private static TableName TABLE_RETRY = TableName.valueOf("TableRetry");
052
053  private static Table tableDoNotRetry;
054
055  private static Table tableRetry;
056
057  private static byte[] CF = Bytes.toBytes("cf");
058
059  private static byte[] CQ = Bytes.toBytes("cq");
060
061  @BeforeAll
062  public static void setUp() throws Exception {
063    UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 2);
064    UTIL.startMiniCluster();
065    UTIL.getAdmin()
066      .createTable(TableDescriptorBuilder.newBuilder(TABLE_DONOT_RETRY)
067        .setCoprocessor(ThrowDoNotRetryIOExceptionCoprocessor.class.getName())
068        .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(CF).build()).build());
069    UTIL.getAdmin()
070      .createTable(TableDescriptorBuilder.newBuilder(TABLE_RETRY)
071        .setCoprocessor(ThrowIOExceptionCoprocessor.class.getName())
072        .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(CF).build()).build());
073    tableDoNotRetry = UTIL.getConnection().getTable(TABLE_DONOT_RETRY);
074    tableRetry = UTIL.getConnection().getTable(TABLE_RETRY);
075  }
076
077  @AfterAll
078  public static void tearDown() throws Exception {
079    UTIL.getAdmin().disableTable(TABLE_DONOT_RETRY);
080    UTIL.getAdmin().disableTable(TABLE_RETRY);
081    UTIL.getAdmin().deleteTable(TABLE_DONOT_RETRY);
082    UTIL.getAdmin().deleteTable(TABLE_RETRY);
083    UTIL.shutdownMiniCluster();
084  }
085
086  @Test
087  public void testGetWithDoNotRetryIOException() throws Exception {
088    assertThrows(DoNotRetryIOException.class,
089      () -> tableDoNotRetry.get(new Get(Bytes.toBytes("row")).addColumn(CF, CQ)));
090  }
091
092  @Test
093  public void testPutWithDoNotRetryIOException() throws Exception {
094    assertThrows(DoNotRetryIOException.class, () -> tableDoNotRetry
095      .put(new Put(Bytes.toBytes("row")).addColumn(CF, CQ, Bytes.toBytes("value"))));
096  }
097
098  @Test
099  public void testDeleteWithDoNotRetryIOException() throws Exception {
100    assertThrows(DoNotRetryIOException.class,
101      () -> tableDoNotRetry.delete(new Delete(Bytes.toBytes("row")).addColumn(CF, CQ)));
102  }
103
104  @Test
105  public void testAppendWithDoNotRetryIOException() throws Exception {
106    assertThrows(DoNotRetryIOException.class, () -> tableDoNotRetry
107      .append(new Append(Bytes.toBytes("row")).addColumn(CF, CQ, Bytes.toBytes("value"))));
108  }
109
110  @Test
111  public void testIncrementWithDoNotRetryIOException() throws Exception {
112    assertThrows(DoNotRetryIOException.class,
113      () -> tableDoNotRetry.increment(new Increment(Bytes.toBytes("row")).addColumn(CF, CQ, 1)));
114  }
115
116  @Test
117  public void testGetWithIOException() throws Exception {
118    assertThrows(RetriesExhaustedException.class,
119      () -> tableRetry.get(new Get(Bytes.toBytes("row")).addColumn(CF, CQ)));
120  }
121
122  @Test
123  public void testPutWithIOException() throws Exception {
124    assertThrows(RetriesExhaustedException.class, () -> tableRetry
125      .put(new Put(Bytes.toBytes("row")).addColumn(CF, CQ, Bytes.toBytes("value"))));
126  }
127
128  @Test
129  public void testDeleteWithIOException() throws Exception {
130    assertThrows(RetriesExhaustedException.class,
131      () -> tableRetry.delete(new Delete(Bytes.toBytes("row")).addColumn(CF, CQ)));
132  }
133
134  @Test
135  public void testAppendWithIOException() throws Exception {
136    assertThrows(RetriesExhaustedException.class, () -> tableRetry
137      .append(new Append(Bytes.toBytes("row")).addColumn(CF, CQ, Bytes.toBytes("value"))));
138  }
139
140  @Test
141  public void testIncrementWithIOException() throws Exception {
142    assertThrows(RetriesExhaustedException.class,
143      () -> tableRetry.increment(new Increment(Bytes.toBytes("row")).addColumn(CF, CQ, 1)));
144  }
145
146  public static class ThrowDoNotRetryIOExceptionCoprocessor
147    implements RegionCoprocessor, RegionObserver {
148
149    public ThrowDoNotRetryIOExceptionCoprocessor() {
150    }
151
152    @Override
153    public Optional<RegionObserver> getRegionObserver() {
154      return Optional.of(this);
155    }
156
157    @Override
158    public void preGetOp(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
159      final Get get, final List<Cell> results) throws IOException {
160      throw new DoNotRetryIOException("Call failed and don't retry");
161    }
162
163    @Override
164    public void prePut(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
165      final Put put, final WALEdit edit, final Durability durability) throws IOException {
166      throw new DoNotRetryIOException("Call failed and don't retry");
167    }
168
169    @Override
170    public void preDelete(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
171      final Delete delete, final WALEdit edit, final Durability durability) throws IOException {
172      throw new DoNotRetryIOException("Call failed and don't retry");
173    }
174
175    @Override
176    public Result preIncrement(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
177      final Increment increment) throws IOException {
178      throw new DoNotRetryIOException("Call failed and don't retry");
179    }
180
181    @Override
182    public Result preAppend(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
183      final Append append) throws IOException {
184      throw new DoNotRetryIOException("Call failed and don't retry");
185    }
186  }
187
188  public static class ThrowIOExceptionCoprocessor implements RegionCoprocessor, RegionObserver {
189
190    public ThrowIOExceptionCoprocessor() {
191    }
192
193    @Override
194    public Optional<RegionObserver> getRegionObserver() {
195      return Optional.of(this);
196    }
197
198    @Override
199    public void preGetOp(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
200      final Get get, final List<Cell> results) throws IOException {
201      throw new IOException("Call failed and retry");
202    }
203
204    @Override
205    public void prePut(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
206      final Put put, final WALEdit edit, final Durability durability) throws IOException {
207      throw new IOException("Call failed and retry");
208    }
209
210    @Override
211    public void preDelete(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
212      final Delete delete, final WALEdit edit, final Durability durability) throws IOException {
213      throw new IOException("Call failed and retry");
214    }
215
216    @Override
217    public Result preIncrement(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
218      final Increment increment) throws IOException {
219      throw new IOException("Call failed and retry");
220    }
221
222    @Override
223    public Result preAppend(final ObserverContext<? extends RegionCoprocessorEnvironment> e,
224      final Append append) throws IOException {
225      throw new IOException("Call failed and retry");
226    }
227  }
228}