1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.procedure2.store.wal;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Random;
27 import java.util.Set;
28
29 import org.apache.commons.cli.CommandLine;
30 import org.apache.commons.cli.Option;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
34 import org.apache.hadoop.hbase.ProcedureInfo;
35 import org.apache.hadoop.hbase.procedure2.Procedure;
36 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
37 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility.TestProcedure;
38 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore;
39 import org.apache.hadoop.hbase.procedure2.store.ProcedureStore.ProcedureIterator;
40 import org.apache.hadoop.hbase.procedure2.util.StringUtils;
41 import org.apache.hadoop.hbase.util.AbstractHBaseTool;
42
43 import static java.lang.System.currentTimeMillis;
44
45 public class ProcedureWALLoaderPerformanceEvaluation extends AbstractHBaseTool {
46 protected static final HBaseCommonTestingUtility UTIL = new HBaseCommonTestingUtility();
47
48
49 public static int DEFAULT_NUM_PROCS = 1000000;
50 public static Option NUM_PROCS_OPTION = new Option("procs", true,
51 "Total number of procedures. Default: " + DEFAULT_NUM_PROCS);
52 public static int DEFAULT_NUM_WALS = 0;
53 public static Option NUM_WALS_OPTION = new Option("wals", true,
54 "Number of WALs to write. If -ve or 0, uses " + WALProcedureStore.ROLL_THRESHOLD_CONF_KEY +
55 " conf to roll the logs. Default: " + DEFAULT_NUM_WALS);
56 public static int DEFAULT_STATE_SIZE = 1024;
57 public static Option STATE_SIZE_OPTION = new Option("size", true,
58 "Size of serialized state in bytes to write on update. Default: " + DEFAULT_STATE_SIZE
59 + " bytes");
60 public static int DEFAULT_UPDATES_PER_PROC = 5;
61 public static Option UPDATES_PER_PROC_OPTION = new Option("updates_per_proc", true,
62 "Number of update states to write for each proc. Default: " + DEFAULT_UPDATES_PER_PROC);
63 public static double DEFAULT_DELETE_PROCS_FRACTION = 0.50;
64 public static Option DELETE_PROCS_FRACTION_OPTION = new Option("delete_procs_fraction", true,
65 "Fraction of procs for which to write delete state. Distribution of procs chosen for "
66 + "delete is uniform across all procs. Default: " + DEFAULT_DELETE_PROCS_FRACTION);
67
68 public int numProcs;
69 public int updatesPerProc;
70 public double deleteProcsFraction;
71 public int numWals;
72 private WALProcedureStore store;
73 static byte[] serializedState;
74
75 private static class LoadCounter implements ProcedureStore.ProcedureLoader {
76 public LoadCounter() {}
77
78 @Override
79 public void setMaxProcId(long maxProcId) {
80 }
81
82 @Override
83 public void load(ProcedureIterator procIter) throws IOException {
84 while (procIter.hasNext()) {
85 if (procIter.isNextCompleted()) {
86 ProcedureInfo proc = procIter.nextAsProcedureInfo();
87 } else {
88 Procedure<?> proc = procIter.nextAsProcedure();
89 }
90 }
91 }
92
93 @Override
94 public void handleCorrupted(ProcedureIterator procIter) throws IOException {
95 while (procIter.hasNext()) {
96 Procedure<?> proc = procIter.nextAsProcedure();
97 }
98 }
99 }
100
101 @Override
102 protected void addOptions() {
103 addOption(NUM_PROCS_OPTION);
104 addOption(UPDATES_PER_PROC_OPTION);
105 addOption(DELETE_PROCS_FRACTION_OPTION);
106 addOption(NUM_WALS_OPTION);
107 addOption(STATE_SIZE_OPTION);
108 }
109
110 @Override
111 protected void processOptions(CommandLine cmd) {
112 numProcs = getOptionAsInt(cmd, NUM_PROCS_OPTION.getOpt(), DEFAULT_NUM_PROCS);
113 numWals = getOptionAsInt(cmd, NUM_WALS_OPTION.getOpt(), DEFAULT_NUM_WALS);
114 int stateSize = getOptionAsInt(cmd, STATE_SIZE_OPTION.getOpt(), DEFAULT_STATE_SIZE);
115 serializedState = new byte[stateSize];
116 updatesPerProc = getOptionAsInt(cmd, UPDATES_PER_PROC_OPTION.getOpt(),
117 DEFAULT_UPDATES_PER_PROC);
118 deleteProcsFraction = getOptionAsDouble(cmd, DELETE_PROCS_FRACTION_OPTION.getOpt(),
119 DEFAULT_DELETE_PROCS_FRACTION);
120 setupConf();
121 }
122
123 private void setupConf() {
124 if (numWals > 0) {
125 conf.setLong(WALProcedureStore.ROLL_THRESHOLD_CONF_KEY, Long.MAX_VALUE);
126 }
127 }
128
129 public void setUpProcedureStore() throws IOException {
130 Path testDir = UTIL.getDataTestDir();
131 FileSystem fs = testDir.getFileSystem(conf);
132 Path logDir = new Path(testDir, "proc-logs");
133 System.out.println("\n\nLogs directory : " + logDir.toString() + "\n\n");
134 fs.delete(logDir, true);
135 store = ProcedureTestingUtility.createWalStore(conf, logDir);
136 store.start(1);
137 store.recoverLease();
138 store.load(new LoadCounter());
139 }
140
141
142
143
144
145
146 private List<Integer> shuffleProcWriteSequence() {
147 Random rand = new Random();
148 List<Integer> procStatesSequence = new ArrayList<>();
149 Set<Integer> toBeDeletedProcs = new HashSet<>();
150
151
152 for (int procId = 1; procId <= numProcs; ++procId) {
153 procStatesSequence.addAll(Collections.nCopies(updatesPerProc + 1, procId));
154 if (rand.nextFloat() < deleteProcsFraction) {
155 procStatesSequence.add(procId);
156 toBeDeletedProcs.add(procId);
157 }
158 }
159 Collections.shuffle(procStatesSequence);
160
161 for (int i = procStatesSequence.size() - 1; i >= 0; --i) {
162 int procId = procStatesSequence.get(i);
163 if (toBeDeletedProcs.contains(procId)) {
164 procStatesSequence.set(i, -1 * procId);
165 toBeDeletedProcs.remove(procId);
166 }
167 }
168 return procStatesSequence;
169 }
170
171 private void writeWals() throws IOException {
172 List<Integer> procStates = shuffleProcWriteSequence();
173 TestProcedure[] procs = new TestProcedure[numProcs + 1];
174 int numProcsPerWal = numWals > 0 ? procStates.size() / numWals : Integer.MAX_VALUE;
175 long startTime = currentTimeMillis();
176 long lastTime = startTime;
177 for (int i = 0; i < procStates.size(); ++i) {
178 int procId = procStates.get(i);
179 if (procId < 0) {
180 store.delete(procs[-procId].getProcId());
181 procs[-procId] = null;
182 } else if (procs[procId] == null) {
183 procs[procId] = new TestProcedure(procId, 0);
184 procs[procId].setData(serializedState);
185 store.insert(procs[procId], null);
186 } else {
187 store.update(procs[procId]);
188 }
189 if (i > 0 && i % numProcsPerWal == 0) {
190 long currentTime = currentTimeMillis();
191 System.out.println("Forcing wall roll. Time taken on last WAL: " +
192 (currentTime - lastTime) / 1000.0f + " sec");
193 store.rollWriterForTesting();
194 lastTime = currentTime;
195 }
196 }
197 long timeTaken = currentTimeMillis() - startTime;
198 System.out.println("\n\nDone writing WALs.\nNum procs : " + numProcs + "\nTotal time taken : "
199 + StringUtils.humanTimeDiff(timeTaken) + "\n\n");
200 }
201
202 private void storeRestart(ProcedureStore.ProcedureLoader loader) throws IOException {
203 System.out.println("Restarting procedure store to read back the WALs");
204 store.stop(false);
205 store.start(1);
206 store.recoverLease();
207
208 long startTime = currentTimeMillis();
209 store.load(loader);
210 long timeTaken = System.currentTimeMillis() - startTime;
211 System.out.println("******************************************");
212 System.out.println("Load time : " + (timeTaken / 1000.0f) + "sec");
213 System.out.println("******************************************");
214 }
215
216 public void tearDownProcedureStore() {
217 store.stop(false);
218 try {
219 store.getFileSystem().delete(store.getWALDir(), true);
220 } catch (IOException e) {
221 System.err.println("Error: Couldn't delete log dir. You can delete it manually to free up "
222 + "disk space. Location: " + store.getWALDir().toString());
223 System.err.println(e.toString());
224 }
225 }
226
227 @Override
228 protected int doWork() {
229 try {
230 setUpProcedureStore();
231 writeWals();
232 storeRestart(new LoadCounter());
233 return EXIT_SUCCESS;
234 } catch (IOException e) {
235 e.printStackTrace();
236 return EXIT_FAILURE;
237 } finally {
238 tearDownProcedureStore();
239 }
240 }
241
242 public static void main(String[] args) throws IOException {
243 ProcedureWALLoaderPerformanceEvaluation tool = new ProcedureWALLoaderPerformanceEvaluation();
244 tool.setConf(UTIL.getConfiguration());
245 tool.run(args);
246 }
247 }