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;
19  
20  import java.io.DataInput;
21  import java.io.DataOutput;
22  import java.io.IOException;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.TreeMap;
28  import java.util.TreeSet;
29  
30  import org.apache.hadoop.hbase.util.Bytes;
31  import org.apache.hadoop.hbase.util.Strings;
32  import org.apache.hadoop.io.VersionedWritable;
33  import org.apache.hadoop.io.WritableComparable;
34  
35  /**
36   * This class is used to export current state of load on a RegionServer.
37   * This is the version of HServerLoad that we had in 0.92.
38   */
39  public class HServerLoad092 extends VersionedWritable
40  implements WritableComparable<HServerLoad092> {
41    private static final byte VERSION = 2;
42    // Empty load instance.
43    public static final HServerLoad092 EMPTY_HSERVERLOAD = new HServerLoad092();
44  
45    /** Number of requests per second since last report.
46     */
47    // TODO: Instead build this up out of region counters.
48    private int numberOfRequests = 0;
49  
50    /** Total Number of requests from the start of the region server.
51     */
52    private int totalNumberOfRequests = 0;
53    
54    /** the amount of used heap, in MB */
55    private int usedHeapMB = 0;
56  
57    /** the maximum allowable size of the heap, in MB */
58    private int maxHeapMB = 0;
59  
60    // Regionserver-level coprocessors, e.g., WALObserver implementations.
61    // Region-level coprocessors, on the other hand, are stored inside RegionLoad
62    // objects.
63    private Set<String> coprocessors =
64        new TreeSet<String>();
65  
66    /**
67     * HBASE-4070: Improve region server metrics to report loaded coprocessors.
68     *
69     * @return Returns the set of all coprocessors on this
70     * regionserver, where this set is the union of the
71     * regionserver-level coprocessors on one hand, and all of the region-level
72     * coprocessors, on the other.
73     *
74     * We must iterate through all regions loaded on this regionserver to
75     * obtain all of the region-level coprocessors.
76     */
77    public String[] getCoprocessors() {
78      TreeSet<String> returnValue = new TreeSet<String>(coprocessors);
79      for (Map.Entry<byte[], RegionLoad> rls: getRegionsLoad().entrySet()) {
80        for (String coprocessor: rls.getValue().getCoprocessors()) {
81          returnValue.add(coprocessor);
82        }
83      }
84      return returnValue.toArray(new String[0]);
85    }
86  
87    /** per-region load metrics */
88    private Map<byte[], RegionLoad> regionLoad =
89      new TreeMap<byte[], RegionLoad>(Bytes.BYTES_COMPARATOR);
90  
91    /** @return the object version number */
92    public byte getVersion() {
93      return VERSION;
94    }
95  
96    /**
97     * Encapsulates per-region loading metrics.
98     */
99    public static class RegionLoad extends VersionedWritable {
100     private static final byte VERSION = 1;
101 
102     /** @return the object version number */
103     public byte getVersion() {
104       return VERSION;
105     }
106 
107     /** the region name */
108     private byte[] name;
109     /** the number of stores for the region */
110     private int stores;
111     /** the number of storefiles for the region */
112     private int storefiles;
113     /** the total size of the store files for the region, uncompressed, in MB */
114     private int storeUncompressedSizeMB;
115     /** the current total size of the store files for the region, in MB */
116     private int storefileSizeMB;
117     /** the current size of the memstore for the region, in MB */
118     private int memstoreSizeMB;
119 
120     /**
121      * The current total size of root-level store file indexes for the region,
122      * in MB. The same as {@link #rootIndexSizeKB} but in MB.
123      */
124     private int storefileIndexSizeMB;
125     /** the current total read requests made to region */
126     private int readRequestsCount;
127     /** the current total write requests made to region */
128     private int writeRequestsCount;
129     /** the total compacting key values in currently running compaction */
130     private long totalCompactingKVs;
131     /** the completed count of key values in currently running compaction */
132     private long currentCompactedKVs;
133 
134     /** The current total size of root-level indexes for the region, in KB. */
135     private int rootIndexSizeKB;
136 
137     /** The total size of all index blocks, not just the root level, in KB. */
138     private int totalStaticIndexSizeKB;
139 
140     /**
141      * The total size of all Bloom filter blocks, not just loaded into the
142      * block cache, in KB.
143      */
144     private int totalStaticBloomSizeKB;
145 
146     // Region-level coprocessors.
147     Set<String> coprocessors =
148         new TreeSet<String>();
149 
150     /**
151      * Constructor, for Writable
152      */
153     public RegionLoad() {
154         super();
155     }
156 
157     /**
158      * @param name
159      * @param stores
160      * @param storefiles
161      * @param storeUncompressedSizeMB
162      * @param storefileSizeMB
163      * @param memstoreSizeMB
164      * @param storefileIndexSizeMB
165      * @param readRequestsCount
166      * @param writeRequestsCount
167      * @param totalCompactingKVs
168      * @param currentCompactedKVs
169      * @param coprocessors
170      */
171     public RegionLoad(final byte[] name, final int stores,
172         final int storefiles, final int storeUncompressedSizeMB,
173         final int storefileSizeMB,
174         final int memstoreSizeMB, final int storefileIndexSizeMB,
175         final int rootIndexSizeKB, final int totalStaticIndexSizeKB,
176         final int totalStaticBloomSizeKB,
177         final int readRequestsCount, final int writeRequestsCount,
178         final long totalCompactingKVs, final long currentCompactedKVs,
179         final Set<String> coprocessors) {
180       this.name = name;
181       this.stores = stores;
182       this.storefiles = storefiles;
183       this.storeUncompressedSizeMB = storeUncompressedSizeMB;
184       this.storefileSizeMB = storefileSizeMB;
185       this.memstoreSizeMB = memstoreSizeMB;
186       this.storefileIndexSizeMB = storefileIndexSizeMB;
187       this.rootIndexSizeKB = rootIndexSizeKB;
188       this.totalStaticIndexSizeKB = totalStaticIndexSizeKB;
189       this.totalStaticBloomSizeKB = totalStaticBloomSizeKB;
190       this.readRequestsCount = readRequestsCount;
191       this.writeRequestsCount = writeRequestsCount;
192       this.totalCompactingKVs = totalCompactingKVs;
193       this.currentCompactedKVs = currentCompactedKVs;
194       this.coprocessors = coprocessors;
195     }
196 
197     // Getters
198     private String[] getCoprocessors() {
199       return coprocessors.toArray(new String[0]);
200     }
201 
202     /**
203      * @return the region name
204      */
205     public byte[] getName() {
206       return name;
207     }
208 
209     /**
210      * @return the region name as a string
211      */
212     public String getNameAsString() {
213       return Bytes.toString(name);
214     }
215 
216     /**
217      * @return the number of stores
218      */
219     public int getStores() {
220       return stores;
221     }
222 
223     /**
224      * @return the number of storefiles
225      */
226     public int getStorefiles() {
227       return storefiles;
228     }
229 
230     /**
231      * @return the total size of the storefiles, in MB
232      */
233     public int getStorefileSizeMB() {
234       return storefileSizeMB;
235     }
236 
237     /**
238      * @return the memstore size, in MB
239      */
240     public int getMemStoreSizeMB() {
241       return memstoreSizeMB;
242     }
243 
244     /**
245      * @return the approximate size of storefile indexes on the heap, in MB
246      */
247     public int getStorefileIndexSizeMB() {
248       return storefileIndexSizeMB;
249     }
250 
251     /**
252      * @return the number of requests made to region
253      */
254     public long getRequestsCount() {
255       return readRequestsCount + writeRequestsCount;
256     }
257 
258     /**
259      * @return the number of read requests made to region
260      */
261     public long getReadRequestsCount() {
262       return readRequestsCount;
263     }
264 
265     /**
266      * @return the number of read requests made to region
267      */
268     public long getWriteRequestsCount() {
269       return writeRequestsCount;
270     }
271 
272     /**
273      * @return the total number of kvs in current compaction
274      */
275     public long getTotalCompactingKVs() {
276       return totalCompactingKVs;
277     }
278 
279     /**
280      * @return the number of already compacted kvs in current compaction
281      */
282     public long getCurrentCompactedKVs() {
283       return currentCompactedKVs;
284     }
285 
286     // Setters
287 
288     /**
289      * @param name the region name
290      */
291     public void setName(byte[] name) {
292       this.name = name;
293     }
294 
295     /**
296      * @param stores the number of stores
297      */
298     public void setStores(int stores) {
299       this.stores = stores;
300     }
301 
302     /**
303      * @param storefiles the number of storefiles
304      */
305     public void setStorefiles(int storefiles) {
306       this.storefiles = storefiles;
307     }
308 
309     /**
310      * @param memstoreSizeMB the memstore size, in MB
311      */
312     public void setMemStoreSizeMB(int memstoreSizeMB) {
313       this.memstoreSizeMB = memstoreSizeMB;
314     }
315 
316     /**
317      * @param storefileIndexSizeMB the approximate size of storefile indexes
318      *  on the heap, in MB
319      */
320     public void setStorefileIndexSizeMB(int storefileIndexSizeMB) {
321       this.storefileIndexSizeMB = storefileIndexSizeMB;
322     }
323 
324     /**
325      * @param requestsCount the number of read requests to region
326      */
327     public void setReadRequestsCount(int requestsCount) {
328       this.readRequestsCount = requestsCount;
329     }
330 
331     /**
332      * @param requestsCount the number of write requests to region
333      */
334     public void setWriteRequestsCount(int requestsCount) {
335       this.writeRequestsCount = requestsCount;
336     }
337 
338     /**
339      * @param totalCompactingKVs the number of kvs total in current compaction
340      */
341     public void setTotalCompactingKVs(long totalCompactingKVs) {
342       this.totalCompactingKVs = totalCompactingKVs;
343     }
344 
345     /**
346      * @param currentCompactedKVs the number of kvs already compacted in
347      * current compaction
348      */
349     public void setCurrentCompactedKVs(long currentCompactedKVs) {
350       this.currentCompactedKVs = currentCompactedKVs;
351     }
352 
353     // Writable
354     public void readFields(DataInput in) throws IOException {
355       super.readFields(in);
356       int version = in.readByte();
357       if (version > VERSION) throw new IOException("Version mismatch; " + version);
358       int namelen = in.readInt();
359       this.name = new byte[namelen];
360       in.readFully(this.name);
361       this.stores = in.readInt();
362       this.storefiles = in.readInt();
363       this.storeUncompressedSizeMB = in.readInt();
364       this.storefileSizeMB = in.readInt();
365       this.memstoreSizeMB = in.readInt();
366       this.storefileIndexSizeMB = in.readInt();
367       this.readRequestsCount = in.readInt();
368       this.writeRequestsCount = in.readInt();
369       this.rootIndexSizeKB = in.readInt();
370       this.totalStaticIndexSizeKB = in.readInt();
371       this.totalStaticBloomSizeKB = in.readInt();
372       this.totalCompactingKVs = in.readLong();
373       this.currentCompactedKVs = in.readLong();
374       int coprocessorsSize = in.readInt();
375       coprocessors = new TreeSet<String>();
376       for (int i = 0; i < coprocessorsSize; i++) {
377         coprocessors.add(in.readUTF());
378       }
379     }
380 
381     public void write(DataOutput out) throws IOException {
382       super.write(out);
383       out.writeByte(VERSION);
384       out.writeInt(name.length);
385       out.write(name);
386       out.writeInt(stores);
387       out.writeInt(storefiles);
388       out.writeInt(storeUncompressedSizeMB);
389       out.writeInt(storefileSizeMB);
390       out.writeInt(memstoreSizeMB);
391       out.writeInt(storefileIndexSizeMB);
392       out.writeInt(readRequestsCount);
393       out.writeInt(writeRequestsCount);
394       out.writeInt(rootIndexSizeKB);
395       out.writeInt(totalStaticIndexSizeKB);
396       out.writeInt(totalStaticBloomSizeKB);
397       out.writeLong(totalCompactingKVs);
398       out.writeLong(currentCompactedKVs);
399       out.writeInt(coprocessors.size());
400       for (String coprocessor: coprocessors) {
401         out.writeUTF(coprocessor);
402       }
403     }
404 
405     /**
406      * @see java.lang.Object#toString()
407      */
408     @Override
409     public String toString() {
410       StringBuilder sb = Strings.appendKeyValue(new StringBuilder(), "numberOfStores",
411         Integer.valueOf(this.stores));
412       sb = Strings.appendKeyValue(sb, "numberOfStorefiles",
413         Integer.valueOf(this.storefiles));
414       sb = Strings.appendKeyValue(sb, "storefileUncompressedSizeMB",
415         Integer.valueOf(this.storeUncompressedSizeMB));
416       sb = Strings.appendKeyValue(sb, "storefileSizeMB",
417           Integer.valueOf(this.storefileSizeMB));
418       if (this.storeUncompressedSizeMB != 0) {
419         sb = Strings.appendKeyValue(sb, "compressionRatio",
420             String.format("%.4f", (float)this.storefileSizeMB/
421                 (float)this.storeUncompressedSizeMB));
422       }
423       sb = Strings.appendKeyValue(sb, "memstoreSizeMB",
424         Integer.valueOf(this.memstoreSizeMB));
425       sb = Strings.appendKeyValue(sb, "storefileIndexSizeMB",
426         Integer.valueOf(this.storefileIndexSizeMB));
427       sb = Strings.appendKeyValue(sb, "readRequestsCount",
428           Long.valueOf(this.readRequestsCount));
429       sb = Strings.appendKeyValue(sb, "writeRequestsCount",
430           Long.valueOf(this.writeRequestsCount));
431       sb = Strings.appendKeyValue(sb, "rootIndexSizeKB",
432           Integer.valueOf(this.rootIndexSizeKB));
433       sb = Strings.appendKeyValue(sb, "totalStaticIndexSizeKB",
434           Integer.valueOf(this.totalStaticIndexSizeKB));
435       sb = Strings.appendKeyValue(sb, "totalStaticBloomSizeKB",
436         Integer.valueOf(this.totalStaticBloomSizeKB));
437       sb = Strings.appendKeyValue(sb, "totalCompactingKVs",
438           Long.valueOf(this.totalCompactingKVs));
439       sb = Strings.appendKeyValue(sb, "currentCompactedKVs",
440           Long.valueOf(this.currentCompactedKVs));
441       float compactionProgressPct = Float.NaN;
442       if( this.totalCompactingKVs > 0 ) {
443         compactionProgressPct = Float.valueOf(
444             this.currentCompactedKVs / this.totalCompactingKVs);
445       }
446       sb = Strings.appendKeyValue(sb, "compactionProgressPct",
447           compactionProgressPct);
448       String coprocessors = Arrays.toString(getCoprocessors());
449       if (coprocessors != null) {
450         sb = Strings.appendKeyValue(sb, "coprocessors",
451             Arrays.toString(getCoprocessors()));
452       }
453       return sb.toString();
454     }
455   }
456 
457   /*
458    * TODO: Other metrics that might be considered when the master is actually
459    * doing load balancing instead of merely trying to decide where to assign
460    * a region:
461    * <ul>
462    *   <li># of CPUs, heap size (to determine the "class" of machine). For
463    *       now, we consider them to be homogeneous.</li>
464    *   <li>#requests per region (Map<{String|HRegionInfo}, Integer>)</li>
465    *   <li>#compactions and/or #splits (churn)</li>
466    *   <li>server death rate (maybe there is something wrong with this server)</li>
467    * </ul>
468    */
469 
470   /** default constructor (used by Writable) */
471   public HServerLoad092() {
472     super();
473   }
474 
475   /**
476    * Constructor
477    * @param numberOfRequests
478    * @param usedHeapMB
479    * @param maxHeapMB
480    * @param coprocessors : coprocessors loaded at the regionserver-level
481    */
482   public HServerLoad092(final int totalNumberOfRequests,
483       final int numberOfRequests, final int usedHeapMB, final int maxHeapMB,
484       final Map<byte[], RegionLoad> regionLoad,
485       final Set<String> coprocessors) {
486     this.numberOfRequests = numberOfRequests;
487     this.usedHeapMB = usedHeapMB;
488     this.maxHeapMB = maxHeapMB;
489     this.regionLoad = regionLoad;
490     this.totalNumberOfRequests = totalNumberOfRequests;
491     this.coprocessors = coprocessors;
492   }
493 
494   /**
495    * Constructor
496    * @param hsl the template HServerLoad
497    */
498   public HServerLoad092(final HServerLoad092 hsl) {
499     this(hsl.totalNumberOfRequests, hsl.numberOfRequests, hsl.usedHeapMB,
500         hsl.maxHeapMB, hsl.getRegionsLoad(), hsl.coprocessors);
501     for (Map.Entry<byte[], RegionLoad> e : hsl.regionLoad.entrySet()) {
502       this.regionLoad.put(e.getKey(), e.getValue());
503     }
504   }
505 
506   /**
507    * Originally, this method factored in the effect of requests going to the
508    * server as well. However, this does not interact very well with the current
509    * region rebalancing code, which only factors number of regions. For the
510    * interim, until we can figure out how to make rebalancing use all the info
511    * available, we're just going to make load purely the number of regions.
512    *
513    * @return load factor for this server
514    */
515   public int getLoad() {
516     // int load = numberOfRequests == 0 ? 1 : numberOfRequests;
517     // load *= numberOfRegions == 0 ? 1 : numberOfRegions;
518     // return load;
519     return this.regionLoad.size();
520   }
521 
522   /**
523    * @see java.lang.Object#toString()
524    */
525   @Override
526   public String toString() {
527     return toString(1);
528   }
529 
530   /**
531    * Returns toString() with the number of requests divided by the message
532    * interval in seconds
533    * @param msgInterval
534    * @return The load as a String
535    */
536   public String toString(int msgInterval) {
537     int numberOfRegions = this.regionLoad.size();
538     StringBuilder sb = new StringBuilder();
539     sb = Strings.appendKeyValue(sb, "requestsPerSecond",
540       Integer.valueOf(numberOfRequests/msgInterval));
541     sb = Strings.appendKeyValue(sb, "numberOfOnlineRegions",
542       Integer.valueOf(numberOfRegions));
543     sb = Strings.appendKeyValue(sb, "usedHeapMB",
544       Integer.valueOf(this.usedHeapMB));
545     sb = Strings.appendKeyValue(sb, "maxHeapMB", Integer.valueOf(maxHeapMB));
546     return sb.toString();
547   }
548 
549   /**
550    * @see java.lang.Object#equals(java.lang.Object)
551    */
552   @Override
553   public boolean equals(Object o) {
554     if (this == o) {
555       return true;
556     }
557     if (o == null) {
558       return false;
559     }
560     if (getClass() != o.getClass()) {
561       return false;
562     }
563     return compareTo((HServerLoad092)o) == 0;
564   }
565 
566   // Getters
567 
568   /**
569    * @return the numberOfRegions
570    */
571   public int getNumberOfRegions() {
572     return this.regionLoad.size();
573   }
574 
575   /**
576    * @return the numberOfRequests per second.
577    */
578   public int getNumberOfRequests() {
579     return numberOfRequests;
580   }
581   
582   /**
583    * @return the numberOfRequests
584    */
585   public int getTotalNumberOfRequests() {
586     return totalNumberOfRequests;
587   }
588 
589   /**
590    * @return the amount of heap in use, in MB
591    */
592   public int getUsedHeapMB() {
593     return usedHeapMB;
594   }
595 
596   /**
597    * @return the maximum allowable heap size, in MB
598    */
599   public int getMaxHeapMB() {
600     return maxHeapMB;
601   }
602 
603   /**
604    * @return region load metrics
605    */
606   public Map<byte[], RegionLoad> getRegionsLoad() {
607     return Collections.unmodifiableMap(regionLoad);
608   }
609 
610   /**
611    * @return Count of storefiles on this regionserver
612    */
613   public int getStorefiles() {
614     int count = 0;
615     for (RegionLoad info: regionLoad.values())
616     	count += info.getStorefiles();
617     return count;
618   }
619 
620   /**
621    * @return Total size of store files in MB
622    */
623   public int getStorefileSizeInMB() {
624     int count = 0;
625     for (RegionLoad info: regionLoad.values())
626       count += info.getStorefileSizeMB();
627     return count;
628   }
629 
630   /**
631    * @return Size of memstores in MB
632    */
633   public int getMemStoreSizeInMB() {
634     int count = 0;
635     for (RegionLoad info: regionLoad.values())
636     	count += info.getMemStoreSizeMB();
637     return count;
638   }
639 
640   /**
641    * @return Size of store file indexes in MB
642    */
643   public int getStorefileIndexSizeInMB() {
644     int count = 0;
645     for (RegionLoad info: regionLoad.values())
646     	count += info.getStorefileIndexSizeMB();
647     return count;
648   }
649 
650   // Writable
651 
652   public void readFields(DataInput in) throws IOException {
653     super.readFields(in);
654     int version = in.readByte();
655     if (version > VERSION) throw new IOException("Version mismatch; " + version);
656     numberOfRequests = in.readInt();
657     usedHeapMB = in.readInt();
658     maxHeapMB = in.readInt();
659     int numberOfRegions = in.readInt();
660     for (int i = 0; i < numberOfRegions; i++) {
661       RegionLoad rl = new RegionLoad();
662       rl.readFields(in);
663       regionLoad.put(rl.getName(), rl);
664     }
665     totalNumberOfRequests = in.readInt();
666     int coprocessorsSize = in.readInt();
667     for(int i = 0; i < coprocessorsSize; i++) {
668       coprocessors.add(in.readUTF());
669     }
670   }
671 
672   public void write(DataOutput out) throws IOException {
673     super.write(out);
674     out.writeByte(VERSION);
675     out.writeInt(numberOfRequests);
676     out.writeInt(usedHeapMB);
677     out.writeInt(maxHeapMB);
678     out.writeInt(this.regionLoad.size());
679     for (RegionLoad rl: regionLoad.values())
680       rl.write(out);
681     out.writeInt(totalNumberOfRequests);
682     out.writeInt(coprocessors.size());
683     for (String coprocessor: coprocessors) {
684       out.writeUTF(coprocessor);
685     }
686   }
687 
688   // Comparable
689 
690   public int compareTo(HServerLoad092 o) {
691     return this.getLoad() - o.getLoad();
692   }
693 }