View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import java.io.IOException;
21  import java.io.PrintWriter;
22  import java.io.StringWriter;
23  import java.security.PrivilegedExceptionAction;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.TableName;
32  import org.apache.hadoop.hbase.client.Append;
33  import org.apache.hadoop.hbase.client.Delete;
34  import org.apache.hadoop.hbase.client.Get;
35  import org.apache.hadoop.hbase.client.HTable;
36  import org.apache.hadoop.hbase.client.Increment;
37  import org.apache.hadoop.hbase.client.Mutation;
38  import org.apache.hadoop.hbase.client.Put;
39  import org.apache.hadoop.hbase.client.Result;
40  import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
41  import org.apache.hadoop.hbase.security.User;
42  import org.apache.hadoop.hbase.util.test.LoadTestDataGenerator;
43  import org.apache.hadoop.security.UserGroupInformation;
44  import org.apache.hadoop.util.StringUtils;
45  
46  /**
47   * A MultiThreadUpdater that helps to work with ACL
48   */
49  public class MultiThreadedUpdaterWithACL extends MultiThreadedUpdater {
50    private static final Log LOG = LogFactory.getLog(MultiThreadedUpdaterWithACL.class);
51    private final static String COMMA= ",";
52    private User userOwner;
53    /**
54     * Maps user with Table instance. Because the table instance has to be created
55     * per user inorder to work in that user's context
56     */
57    private Map<String, HTable> userVsTable = new HashMap<String, HTable>();
58    private Map<String, User> users = new HashMap<String, User>();
59    private String[] userNames;
60  
61    public MultiThreadedUpdaterWithACL(LoadTestDataGenerator dataGen, Configuration conf,
62        TableName tableName, double updatePercent, User userOwner) {
63      super(dataGen, conf, tableName, updatePercent);
64      this.userOwner = userOwner;
65      userNames = dataGenerator.getArgs()[0].split(COMMA);
66    }
67  
68    @Override
69    protected void addUpdaterThreads(int numThreads) throws IOException {
70      for (int i = 0; i < numThreads; ++i) {
71        HBaseUpdaterThread updater = new HBaseUpdaterThreadWithACL(i);
72        updaters.add(updater);
73      }
74    }
75  
76    public class HBaseUpdaterThreadWithACL extends HBaseUpdaterThread {
77  
78      private HTable table;
79      private MutateAccessAction mutateAction = new MutateAccessAction();
80  
81      public HBaseUpdaterThreadWithACL(int updaterId) throws IOException {
82        super(updaterId);
83      }
84  
85      @Override
86      protected HTable createTable() throws IOException {
87        return null;
88      }
89  
90      @Override
91      protected void closeHTable() {
92        try {
93          if (table != null) {
94            table.close();
95          }
96          for (HTable table : userVsTable.values()) {
97            try {
98              table.close();
99            } catch (Exception e) {
100             LOG.error("Error while closing the table " + table.getName(), e);
101           }
102         }
103       } catch (Exception e) {
104         LOG.error("Error while closing the HTable "+table.getName(), e);
105       }
106     }
107 
108     @Override
109     protected Result getRow(final Get get, final long rowKeyBase, final byte[] cf) {
110       PrivilegedExceptionAction<Object> action = new PrivilegedExceptionAction<Object>() {
111 
112         @Override
113         public Object run() throws Exception {
114           Result res = null;
115           HTable localTable = null;
116           try {
117             int mod = ((int) rowKeyBase % userNames.length);
118             if (userVsTable.get(userNames[mod]) == null) {
119               localTable = new HTable(conf, tableName);
120               userVsTable.put(userNames[mod], localTable);
121               res = localTable.get(get);
122             } else {
123               localTable = userVsTable.get(userNames[mod]);
124               res = localTable.get(get);
125             }
126           } catch (IOException ie) {
127             LOG.warn("Failed to get the row for key = [" + get.getRow() + "], column family = ["
128                 + Bytes.toString(cf) + "]", ie);
129           }
130           return res;
131         }
132       };
133 
134       if (userNames != null && userNames.length > 0) {
135         int mod = ((int) rowKeyBase % userNames.length);
136         User user;
137         if(!users.containsKey(userNames[mod])) {
138           UserGroupInformation realUserUgi = UserGroupInformation.createRemoteUser(userNames[mod]);
139           user = User.create(realUserUgi);
140           users.put(userNames[mod], user);
141         } else {
142           user = users.get(userNames[mod]);
143         }
144         try {
145           Result result = (Result) user.runAs(action);
146           return result;
147         } catch (Exception ie) {
148           LOG.warn("Failed to get the row for key = [" + get.getRow() + "], column family = ["
149               + Bytes.toString(cf) + "]", ie);
150         }
151       }
152       // This means that no users were present
153       return null;
154     }
155 
156     @Override
157     public void mutate(final HTable table, Mutation m, final long keyBase, final byte[] row,
158         final byte[] cf, final byte[] q, final byte[] v) {
159       final long start = System.currentTimeMillis();
160       try {
161         m = dataGenerator.beforeMutate(keyBase, m);
162         mutateAction.setMutation(m);
163         mutateAction.setCF(cf);
164         mutateAction.setRow(row);
165         mutateAction.setQualifier(q);
166         mutateAction.setValue(v);
167         mutateAction.setStartTime(start);
168         mutateAction.setKeyBase(keyBase);
169         userOwner.runAs(mutateAction);
170       } catch (IOException e) {
171         recordFailure(m, keyBase, start, e);
172       } catch (InterruptedException e) {
173         failedKeySet.add(keyBase);
174       }
175     }
176 
177     class MutateAccessAction implements PrivilegedExceptionAction<Object> {
178       private HTable table;
179       private long start;
180       private Mutation m;
181       private long keyBase;
182       private byte[] row;
183       private byte[] cf;
184       private byte[] q;
185       private byte[] v;
186 
187       public MutateAccessAction() {
188 
189       }
190 
191       public void setStartTime(final long start) {
192         this.start = start;
193       }
194 
195       public void setMutation(final Mutation m) {
196         this.m = m;
197       }
198 
199       public void setRow(final byte[] row) {
200         this.row = row;
201       }
202 
203       public void setCF(final byte[] cf) {
204         this.cf = cf;
205       }
206 
207       public void setQualifier(final byte[] q) {
208         this.q = q;
209       }
210 
211       public void setValue(final byte[] v) {
212         this.v = v;
213       }
214 
215       public void setKeyBase(final long keyBase) {
216         this.keyBase = keyBase;
217       }
218 
219       @Override
220       public Object run() throws Exception {
221         try {
222           if (table == null) {
223             table = new HTable(conf, tableName);
224           }
225           if (m instanceof Increment) {
226             table.increment((Increment) m);
227           } else if (m instanceof Append) {
228             table.append((Append) m);
229           } else if (m instanceof Put) {
230             table.checkAndPut(row, cf, q, v, (Put) m);
231           } else if (m instanceof Delete) {
232             table.checkAndDelete(row, cf, q, v, (Delete) m);
233           } else {
234             throw new IllegalArgumentException("unsupported mutation "
235                 + m.getClass().getSimpleName());
236           }
237           totalOpTimeMs.addAndGet(System.currentTimeMillis() - start);
238         } catch (IOException e) {
239           recordFailure(m, keyBase, start, e);
240         }
241         return null;
242       }
243     }
244 
245     private void recordFailure(final Mutation m, final long keyBase,
246         final long start, IOException e) {
247       failedKeySet.add(keyBase);
248       String exceptionInfo;
249       if (e instanceof RetriesExhaustedWithDetailsException) {
250         RetriesExhaustedWithDetailsException aggEx = (RetriesExhaustedWithDetailsException) e;
251         exceptionInfo = aggEx.getExhaustiveDescription();
252       } else {
253         StringWriter stackWriter = new StringWriter();
254         PrintWriter pw = new PrintWriter(stackWriter);
255         e.printStackTrace(pw);
256         pw.flush();
257         exceptionInfo = StringUtils.stringifyException(e);
258       }
259       LOG.error("Failed to mutate: " + keyBase + " after " + (System.currentTimeMillis() - start)
260           + "ms; region information: " + getRegionDebugInfoSafe(table, m.getRow()) + "; errors: "
261           + exceptionInfo);
262     }
263   }
264 }