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.regionserver;
019
020import java.util.List;
021import org.apache.hadoop.hbase.Cell;
022import org.apache.hadoop.hbase.HBaseInterfaceAudience;
023import org.apache.hadoop.hbase.client.metrics.ServerSideScanMetrics;
024import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.apache.yetus.audience.InterfaceStability;
027
028/**
029 * ScannerContext instances encapsulate limit tracking AND progress towards those limits during
030 * invocations of {@link InternalScanner#next(java.util.List)} and
031 * {@link RegionScanner#next(java.util.List)}.
032 * <p>
033 * A ScannerContext instance should be updated periodically throughout execution whenever progress
034 * towards a limit has been made. Each limit can be checked via the appropriate checkLimit method.
035 * <p>
036 * Once a limit has been reached, the scan will stop. The invoker of
037 * {@link InternalScanner#next(java.util.List)} or {@link RegionScanner#next(java.util.List)} can
038 * use the appropriate check*Limit methods to see exactly which limits have been reached.
039 * Alternatively, {@link #checkAnyLimitReached(LimitScope)} is provided to see if ANY limit was
040 * reached
041 * <p>
042 * {@link NoLimitScannerContext#NO_LIMIT} is an immutable static definition that can be used
043 * whenever a {@link ScannerContext} is needed but limits do not need to be enforced.
044 * <p>
045 * NOTE: It is important that this class only ever expose setter methods that can be safely skipped
046 * when limits should be NOT enforced. This is because of the necessary immutability of the class
047 * {@link NoLimitScannerContext}. If a setter cannot be safely skipped, the immutable nature of
048 * {@link NoLimitScannerContext} will lead to incorrect behavior.
049 */
050@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
051@InterfaceStability.Evolving
052public class ScannerContext {
053
054  LimitFields limits;
055  /**
056   * A different set of progress fields. Only include batch, dataSize and heapSize. Compare to
057   * LimitFields, ProgressFields doesn't contain time field. As we save a deadline in LimitFields,
058   * so use {@link EnvironmentEdgeManager#currentTime()} directly when check time limit.
059   */
060  ProgressFields progress;
061
062  /**
063   * The state of the scanner after the invocation of {@link InternalScanner#next(java.util.List)}
064   * or {@link RegionScanner#next(java.util.List)}.
065   */
066  NextState scannerState;
067  private static final NextState DEFAULT_STATE = NextState.MORE_VALUES;
068
069  /**
070   * Used as an indication to invocations of {@link InternalScanner#next(java.util.List)} and
071   * {@link RegionScanner#next(java.util.List)} that, if true, the progress tracked within this
072   * {@link ScannerContext} instance should be considered while evaluating the limits. Useful for
073   * enforcing a set of limits across multiple calls (i.e. the limit may not be reached in a single
074   * invocation, but any progress made should be considered in future invocations)
075   * <p>
076   * Defaulting this value to false means that, by default, any tracked progress will be wiped clean
077   * on invocations to {@link InternalScanner#next(java.util.List)} and
078   * {@link RegionScanner#next(java.util.List)} and the call will be treated as though no progress
079   * has been made towards the limits so far.
080   * <p>
081   * This is an important mechanism. Users of Internal/Region scanners expect that they can define
082   * some limits and then repeatedly invoke {@link InternalScanner#next(List)} or
083   * {@link RegionScanner#next(List)} where each invocation respects these limits separately.
084   * <p>
085   * For example:
086   *
087   * <pre>
088   *  {@code
089   * ScannerContext context = new ScannerContext.newBuilder().setBatchLimit(5).build();
090   * RegionScanner scanner = ...
091   * List<Cell> results = new ArrayList<Cell>();
092   * while(scanner.next(results, context)) {
093   *   // Do something with a batch of 5 cells
094   * }
095   * }
096   * </pre>
097   *
098   * However, in the case of RPCs, the server wants to be able to define a set of limits for a
099   * particular RPC request and have those limits respected across multiple invocations. This means
100   * that the progress made towards the limits in earlier calls will be saved and considered in
101   * future invocations
102   */
103  boolean keepProgress;
104  private static boolean DEFAULT_KEEP_PROGRESS = false;
105
106  /**
107   * Allows temporarily ignoring limits and skipping tracking of batch and size progress. Used when
108   * skipping to the next row, in which case all processed cells are thrown away so should not count
109   * towards progress.
110   */
111  boolean skippingRow = false;
112
113  private Cell lastPeekedCell = null;
114
115  // Set this to true will have the same behavior with reaching the time limit.
116  // This is used when you want to make the current RSRpcService.scan returns immediately. For
117  // example, when we want to switch from pread to stream, we can only do it after the rpc call is
118  // returned.
119  private boolean returnImmediately;
120
121  /**
122   * Tracks the relevant server side metrics during scans. null when metrics should not be tracked
123   */
124  final ServerSideScanMetrics metrics;
125
126  ScannerContext(boolean keepProgress, LimitFields limitsToCopy, boolean trackMetrics) {
127    this.limits = new LimitFields();
128    if (limitsToCopy != null) {
129      this.limits.copy(limitsToCopy);
130    }
131
132    // Progress fields are initialized to 0
133    progress = new ProgressFields(0, 0, 0, 0);
134
135    this.keepProgress = keepProgress;
136    this.scannerState = DEFAULT_STATE;
137    this.metrics = trackMetrics ? new ServerSideScanMetrics() : null;
138  }
139
140  public boolean isTrackingMetrics() {
141    return this.metrics != null;
142  }
143
144  /**
145   * Get the metrics instance. Should only be called after a call to {@link #isTrackingMetrics()}
146   * has been made to confirm that metrics are indeed being tracked.
147   * @return {@link ServerSideScanMetrics} instance that is tracking metrics for this scan
148   */
149  public ServerSideScanMetrics getMetrics() {
150    assert isTrackingMetrics();
151    return this.metrics;
152  }
153
154  /**
155   * @return true if the progress tracked so far in this instance will be considered during an
156   *         invocation of {@link InternalScanner#next(java.util.List)} or
157   *         {@link RegionScanner#next(java.util.List)}. false when the progress tracked so far
158   *         should not be considered and should instead be wiped away via {@link #clearProgress()}.
159   *         This only applies to per-row progress, like batch and data/heap size. Block size is
160   *         never reset because it tracks all of the blocks scanned for an entire request.
161   */
162  boolean getKeepProgress() {
163    return keepProgress;
164  }
165
166  void setKeepProgress(boolean keepProgress) {
167    this.keepProgress = keepProgress;
168  }
169
170  /**
171   * In this mode, only block size progress is tracked, and limits are ignored. We set this mode
172   * when skipping to next row, in which case all cells returned a thrown away so should not count
173   * towards progress.
174   * @return true if we are in skipping row mode.
175   */
176  public boolean getSkippingRow() {
177    return skippingRow;
178  }
179
180  /**
181   * @param skippingRow set true to cause disabling of collecting per-cell progress or enforcing any
182   *                    limits. This is used when trying to skip over all cells in a row, in which
183   *                    case those cells are thrown away so should not count towards progress.
184   */
185  void setSkippingRow(boolean skippingRow) {
186    this.skippingRow = skippingRow;
187  }
188
189  /**
190   * Progress towards the batch limit has been made. Increment internal tracking of batch progress
191   */
192  void incrementBatchProgress(int batch) {
193    if (skippingRow) {
194      return;
195    }
196    int currentBatch = progress.getBatch();
197    progress.setBatch(currentBatch + batch);
198  }
199
200  /**
201   * Progress towards the size limit has been made. Increment internal tracking of size progress
202   */
203  void incrementSizeProgress(long dataSize, long heapSize) {
204    if (skippingRow) {
205      return;
206    }
207    long curDataSize = progress.getDataSize();
208    progress.setDataSize(curDataSize + dataSize);
209    long curHeapSize = progress.getHeapSize();
210    progress.setHeapSize(curHeapSize + heapSize);
211  }
212
213  /**
214   * Progress towards the block limit has been made. Increment internal track of block progress
215   */
216  void incrementBlockProgress(int blockSize) {
217    if (blockSize > 0) {
218      long curBlockSize = progress.getBlockSize();
219      progress.setBlockSize(curBlockSize + blockSize);
220    }
221  }
222
223  int getBatchProgress() {
224    return progress.getBatch();
225  }
226
227  long getDataSizeProgress() {
228    return progress.getDataSize();
229  }
230
231  long getHeapSizeProgress() {
232    return progress.getHeapSize();
233  }
234
235  long getBlockSizeProgress() {
236    return progress.getBlockSize();
237  }
238
239  void setProgress(int batchProgress, long sizeProgress, long heapSizeProgress) {
240    setBatchProgress(batchProgress);
241    setSizeProgress(sizeProgress, heapSizeProgress);
242  }
243
244  void setSizeProgress(long dataSizeProgress, long heapSizeProgress) {
245    progress.setDataSize(dataSizeProgress);
246    progress.setHeapSize(heapSizeProgress);
247  }
248
249  void setBatchProgress(int batchProgress) {
250    progress.setBatch(batchProgress);
251  }
252
253  /**
254   * Clear away any progress that has been made so far. All progress fields are reset to initial
255   * values. Only clears progress that should reset between rows. {@link #getBlockSizeProgress()} is
256   * not reset because it increments for all blocks scanned whether the result is included or
257   * filtered.
258   */
259  void clearProgress() {
260    progress.setFields(0, 0, 0, getBlockSizeProgress());
261  }
262
263  /**
264   * Note that this is not a typical setter. This setter returns the {@link NextState} that was
265   * passed in so that methods can be invoked against the new state. Furthermore, this pattern
266   * allows the {@link NoLimitScannerContext} to cleanly override this setter and simply return the
267   * new state, thus preserving the immutability of {@link NoLimitScannerContext}
268   * @return The state that was passed in.
269   */
270  NextState setScannerState(NextState state) {
271    if (!NextState.isValidState(state)) {
272      throw new IllegalArgumentException("Cannot set to invalid state: " + state);
273    }
274
275    this.scannerState = state;
276    return state;
277  }
278
279  /**
280   * @return true when we have more cells for the current row. This usually because we have reached
281   *         a limit in the middle of a row
282   */
283  boolean mayHaveMoreCellsInRow() {
284    return scannerState == NextState.SIZE_LIMIT_REACHED_MID_ROW
285      || scannerState == NextState.TIME_LIMIT_REACHED_MID_ROW
286      || scannerState == NextState.BATCH_LIMIT_REACHED;
287  }
288
289  /** Returns true if the batch limit can be enforced in the checker's scope */
290  boolean hasBatchLimit(LimitScope checkerScope) {
291    return limits.canEnforceBatchLimitFromScope(checkerScope) && limits.getBatch() > 0;
292  }
293
294  /** Returns true if the size limit can be enforced in the checker's scope */
295  boolean hasSizeLimit(LimitScope checkerScope) {
296    return limits.canEnforceSizeLimitFromScope(checkerScope)
297      && (limits.getDataSize() > 0 || limits.getHeapSize() > 0 || limits.getBlockSize() > 0);
298  }
299
300  /** Returns true if the time limit can be enforced in the checker's scope */
301  boolean hasTimeLimit(LimitScope checkerScope) {
302    return limits.canEnforceTimeLimitFromScope(checkerScope)
303      && (limits.getTime() > 0 || returnImmediately);
304  }
305
306  /** Returns true if any limit can be enforced within the checker's scope */
307  boolean hasAnyLimit(LimitScope checkerScope) {
308    return hasBatchLimit(checkerScope) || hasSizeLimit(checkerScope) || hasTimeLimit(checkerScope);
309  }
310
311  /**
312   * @param scope The scope in which the size limit will be enforced
313   */
314  void setSizeLimitScope(LimitScope scope) {
315    limits.setSizeScope(scope);
316  }
317
318  /**
319   * @param scope The scope in which the time limit will be enforced
320   */
321  void setTimeLimitScope(LimitScope scope) {
322    limits.setTimeScope(scope);
323  }
324
325  int getBatchLimit() {
326    return limits.getBatch();
327  }
328
329  long getDataSizeLimit() {
330    return limits.getDataSize();
331  }
332
333  long getTimeLimit() {
334    return limits.getTime();
335  }
336
337  /**
338   * @param checkerScope The scope that the limit is being checked from
339   * @return true when the limit is enforceable from the checker's scope and it has been reached
340   */
341  boolean checkBatchLimit(LimitScope checkerScope) {
342    return !skippingRow && hasBatchLimit(checkerScope) && progress.getBatch() >= limits.getBatch();
343  }
344
345  /**
346   * @param checkerScope The scope that the limit is being checked from
347   * @return true when the limit is enforceable from the checker's scope and it has been reached
348   */
349  boolean checkSizeLimit(LimitScope checkerScope) {
350    return !skippingRow && hasSizeLimit(checkerScope)
351      && (progress.getDataSize() >= limits.getDataSize()
352        || progress.getHeapSize() >= limits.getHeapSize()
353        || progress.getBlockSize() >= limits.getBlockSize());
354  }
355
356  /**
357   * @param checkerScope The scope that the limit is being checked from. The time limit is always
358   *                     checked against {@link EnvironmentEdgeManager.currentTime}
359   * @return true when the limit is enforceable from the checker's scope and it has been reached
360   */
361  boolean checkTimeLimit(LimitScope checkerScope) {
362    return !skippingRow && hasTimeLimit(checkerScope)
363      && (returnImmediately || EnvironmentEdgeManager.currentTime() >= limits.getTime());
364  }
365
366  /**
367   * @param checkerScope The scope that the limits are being checked from
368   * @return true when some limit is enforceable from the checker's scope and it has been reached
369   */
370  boolean checkAnyLimitReached(LimitScope checkerScope) {
371    return checkSizeLimit(checkerScope) || checkBatchLimit(checkerScope)
372      || checkTimeLimit(checkerScope);
373  }
374
375  Cell getLastPeekedCell() {
376    return lastPeekedCell;
377  }
378
379  void setLastPeekedCell(Cell lastPeekedCell) {
380    this.lastPeekedCell = lastPeekedCell;
381  }
382
383  void returnImmediately() {
384    this.returnImmediately = true;
385  }
386
387  @Override
388  public String toString() {
389    StringBuilder sb = new StringBuilder();
390    sb.append("{");
391
392    sb.append("limits:");
393    sb.append(limits);
394
395    sb.append(", progress:");
396    sb.append(progress);
397
398    sb.append(", keepProgress:");
399    sb.append(keepProgress);
400
401    sb.append(", state:");
402    sb.append(scannerState);
403
404    sb.append("}");
405    return sb.toString();
406  }
407
408  public static Builder newBuilder() {
409    return new Builder();
410  }
411
412  public static Builder newBuilder(boolean keepProgress) {
413    return new Builder(keepProgress);
414  }
415
416  public static final class Builder {
417    boolean keepProgress = DEFAULT_KEEP_PROGRESS;
418    boolean trackMetrics = false;
419    LimitFields limits = new LimitFields();
420
421    private Builder() {
422    }
423
424    private Builder(boolean keepProgress) {
425      this.keepProgress = keepProgress;
426    }
427
428    public Builder setKeepProgress(boolean keepProgress) {
429      this.keepProgress = keepProgress;
430      return this;
431    }
432
433    public Builder setTrackMetrics(boolean trackMetrics) {
434      this.trackMetrics = trackMetrics;
435      return this;
436    }
437
438    public Builder setSizeLimit(LimitScope sizeScope, long dataSizeLimit, long heapSizeLimit,
439      long blockSizeLimit) {
440      limits.setDataSize(dataSizeLimit);
441      limits.setHeapSize(heapSizeLimit);
442      limits.setSizeScope(sizeScope);
443      limits.setBlockSize(blockSizeLimit);
444      return this;
445    }
446
447    public Builder setTimeLimit(LimitScope timeScope, long timeLimit) {
448      limits.setTime(timeLimit);
449      limits.setTimeScope(timeScope);
450      return this;
451    }
452
453    public Builder setBatchLimit(int batchLimit) {
454      limits.setBatch(batchLimit);
455      return this;
456    }
457
458    public ScannerContext build() {
459      return new ScannerContext(keepProgress, limits, trackMetrics);
460    }
461  }
462
463  /**
464   * The possible states a scanner may be in following a call to {@link InternalScanner#next(List)}
465   */
466  public enum NextState {
467    MORE_VALUES(true, false),
468    NO_MORE_VALUES(false, false),
469    SIZE_LIMIT_REACHED(true, true),
470
471    /**
472     * Special case of size limit reached to indicate that the size limit was reached in the middle
473     * of a row and thus a partial results was formed
474     */
475    SIZE_LIMIT_REACHED_MID_ROW(true, true),
476    TIME_LIMIT_REACHED(true, true),
477
478    /**
479     * Special case of time limit reached to indicate that the time limit was reached in the middle
480     * of a row and thus a partial results was formed
481     */
482    TIME_LIMIT_REACHED_MID_ROW(true, true),
483    BATCH_LIMIT_REACHED(true, true);
484
485    private final boolean moreValues;
486    private final boolean limitReached;
487
488    private NextState(boolean moreValues, boolean limitReached) {
489      this.moreValues = moreValues;
490      this.limitReached = limitReached;
491    }
492
493    /**
494     * @return true when the state indicates that more values may follow those that have been
495     *         returned
496     */
497    public boolean hasMoreValues() {
498      return this.moreValues;
499    }
500
501    /** Returns true when the state indicates that a limit has been reached and scan should stop */
502    public boolean limitReached() {
503      return this.limitReached;
504    }
505
506    public static boolean isValidState(NextState state) {
507      return state != null;
508    }
509
510    public static boolean hasMoreValues(NextState state) {
511      return isValidState(state) && state.hasMoreValues();
512    }
513  }
514
515  /**
516   * The various scopes where a limit can be enforced. Used to differentiate when a limit should be
517   * enforced or not.
518   */
519  public enum LimitScope {
520    /**
521     * Enforcing a limit between rows means that the limit will not be considered until all the
522     * cells for a particular row have been retrieved
523     */
524    BETWEEN_ROWS(0),
525
526    /**
527     * Enforcing a limit between cells means that the limit will be considered after each full cell
528     * has been retrieved
529     */
530    BETWEEN_CELLS(1);
531
532    /**
533     * When enforcing a limit, we must check that the scope is appropriate for enforcement.
534     * <p>
535     * To communicate this concept, each scope has a depth. A limit will be enforced if the depth of
536     * the checker's scope is less than or equal to the limit's scope. This means that when checking
537     * limits, the checker must know their own scope (i.e. are they checking the limits between
538     * rows, between cells, etc...)
539     */
540    final int depth;
541
542    LimitScope(int depth) {
543      this.depth = depth;
544    }
545
546    final int depth() {
547      return depth;
548    }
549
550    /**
551     * @param checkerScope The scope in which the limit is being checked
552     * @return true when the checker is in a scope that indicates the limit can be enforced. Limits
553     *         can be enforced from "higher or equal" scopes (i.e. the checker's scope is at a
554     *         lesser depth than the limit)
555     */
556    boolean canEnforceLimitFromScope(LimitScope checkerScope) {
557      return checkerScope != null && checkerScope.depth() <= depth;
558    }
559  }
560
561  /**
562   * The different fields that can be used as limits in calls to
563   * {@link InternalScanner#next(java.util.List)} and {@link RegionScanner#next(java.util.List)}
564   */
565  private static class LimitFields {
566    /**
567     * Default values of the limit fields. Defined such that if a field does NOT change from its
568     * default, it will not be enforced
569     */
570    private static int DEFAULT_BATCH = -1;
571    private static long DEFAULT_SIZE = -1L;
572    private static long DEFAULT_TIME = -1L;
573
574    /**
575     * Default scope that is assigned to a limit if a scope is not specified.
576     */
577    private static final LimitScope DEFAULT_SCOPE = LimitScope.BETWEEN_ROWS;
578
579    // The batch limit will always be enforced between cells, thus, there isn't a field to hold the
580    // batch scope
581    int batch = DEFAULT_BATCH;
582
583    LimitScope sizeScope = DEFAULT_SCOPE;
584    // The sum of cell data sizes(key + value). The Cell data might be in on heap or off heap area.
585    long dataSize = DEFAULT_SIZE;
586    // The sum of heap space occupied by all tracked cells. This includes Cell POJO's overhead as
587    // such AND data cells of Cells which are in on heap area.
588    long heapSize = DEFAULT_SIZE;
589    // The total amount of block bytes that have been loaded in order to process cells for the
590    // request.
591    long blockSize = DEFAULT_SIZE;
592
593    LimitScope timeScope = DEFAULT_SCOPE;
594    long time = DEFAULT_TIME;
595
596    /**
597     * Fields keep their default values.
598     */
599    LimitFields() {
600    }
601
602    void copy(LimitFields limitsToCopy) {
603      if (limitsToCopy != null) {
604        setFields(limitsToCopy.getBatch(), limitsToCopy.getSizeScope(), limitsToCopy.getDataSize(),
605          limitsToCopy.getHeapSize(), limitsToCopy.getBlockSize(), limitsToCopy.getTimeScope(),
606          limitsToCopy.getTime());
607      }
608    }
609
610    /**
611     * Set all fields together.
612     */
613    void setFields(int batch, LimitScope sizeScope, long dataSize, long heapSize, long blockSize,
614      LimitScope timeScope, long time) {
615      setBatch(batch);
616      setSizeScope(sizeScope);
617      setDataSize(dataSize);
618      setHeapSize(heapSize);
619      setBlockSize(blockSize);
620      setTimeScope(timeScope);
621      setTime(time);
622    }
623
624    int getBatch() {
625      return this.batch;
626    }
627
628    void setBatch(int batch) {
629      this.batch = batch;
630    }
631
632    /** Returns true when the limit can be enforced from the scope of the checker */
633    boolean canEnforceBatchLimitFromScope(LimitScope checkerScope) {
634      return LimitScope.BETWEEN_CELLS.canEnforceLimitFromScope(checkerScope);
635    }
636
637    long getDataSize() {
638      return this.dataSize;
639    }
640
641    long getHeapSize() {
642      return this.heapSize;
643    }
644
645    long getBlockSize() {
646      return this.blockSize;
647    }
648
649    void setDataSize(long dataSize) {
650      this.dataSize = dataSize;
651    }
652
653    void setHeapSize(long heapSize) {
654      this.heapSize = heapSize;
655    }
656
657    void setBlockSize(long blockSize) {
658      this.blockSize = blockSize;
659    }
660
661    /** Returns {@link LimitScope} indicating scope in which the size limit is enforced */
662    LimitScope getSizeScope() {
663      return this.sizeScope;
664    }
665
666    /**
667     * Change the scope in which the size limit is enforced
668     */
669    void setSizeScope(LimitScope scope) {
670      this.sizeScope = scope;
671    }
672
673    /** Returns true when the limit can be enforced from the scope of the checker */
674    boolean canEnforceSizeLimitFromScope(LimitScope checkerScope) {
675      return this.sizeScope.canEnforceLimitFromScope(checkerScope);
676    }
677
678    long getTime() {
679      return this.time;
680    }
681
682    void setTime(long time) {
683      this.time = time;
684    }
685
686    /** Returns {@link LimitScope} indicating scope in which the time limit is enforced */
687    LimitScope getTimeScope() {
688      return this.timeScope;
689    }
690
691    /**
692     * Change the scope in which the time limit is enforced
693     */
694    void setTimeScope(LimitScope scope) {
695      this.timeScope = scope;
696    }
697
698    /** Returns true when the limit can be enforced from the scope of the checker */
699    boolean canEnforceTimeLimitFromScope(LimitScope checkerScope) {
700      return this.timeScope.canEnforceLimitFromScope(checkerScope);
701    }
702
703    @Override
704    public String toString() {
705      StringBuilder sb = new StringBuilder();
706      sb.append("{");
707
708      sb.append("batch:");
709      sb.append(batch);
710
711      sb.append(", dataSize:");
712      sb.append(dataSize);
713
714      sb.append(", heapSize:");
715      sb.append(heapSize);
716
717      sb.append(", blockSize:");
718      sb.append(blockSize);
719
720      sb.append(", sizeScope:");
721      sb.append(sizeScope);
722
723      sb.append(", time:");
724      sb.append(time);
725
726      sb.append(", timeScope:");
727      sb.append(timeScope);
728
729      sb.append("}");
730      return sb.toString();
731    }
732  }
733
734  private static class ProgressFields {
735
736    private static int DEFAULT_BATCH = -1;
737    private static long DEFAULT_SIZE = -1L;
738
739    // The batch limit will always be enforced between cells, thus, there isn't a field to hold the
740    // batch scope
741    int batch = DEFAULT_BATCH;
742
743    // The sum of cell data sizes(key + value). The Cell data might be in on heap or off heap area.
744    long dataSize = DEFAULT_SIZE;
745    // The sum of heap space occupied by all tracked cells. This includes Cell POJO's overhead as
746    // such AND data cells of Cells which are in on heap area.
747    long heapSize = DEFAULT_SIZE;
748    // The total amount of block bytes that have been loaded in order to process cells for the
749    // request.
750    long blockSize = DEFAULT_SIZE;
751
752    ProgressFields(int batch, long size, long heapSize, long blockSize) {
753      setFields(batch, size, heapSize, blockSize);
754    }
755
756    /**
757     * Set all fields together.
758     */
759    void setFields(int batch, long dataSize, long heapSize, long blockSize) {
760      setBatch(batch);
761      setDataSize(dataSize);
762      setHeapSize(heapSize);
763      setBlockSize(blockSize);
764    }
765
766    int getBatch() {
767      return this.batch;
768    }
769
770    void setBatch(int batch) {
771      this.batch = batch;
772    }
773
774    long getDataSize() {
775      return this.dataSize;
776    }
777
778    long getHeapSize() {
779      return this.heapSize;
780    }
781
782    long getBlockSize() {
783      return this.blockSize;
784    }
785
786    void setDataSize(long dataSize) {
787      this.dataSize = dataSize;
788    }
789
790    void setBlockSize(long blockSize) {
791      this.blockSize = blockSize;
792    }
793
794    void setHeapSize(long heapSize) {
795      this.heapSize = heapSize;
796    }
797
798    @Override
799    public String toString() {
800      StringBuilder sb = new StringBuilder();
801      sb.append("{");
802
803      sb.append("batch:");
804      sb.append(batch);
805
806      sb.append(", dataSize:");
807      sb.append(dataSize);
808
809      sb.append(", heapSize:");
810      sb.append(heapSize);
811
812      sb.append(", blockSize:");
813      sb.append(blockSize);
814
815      sb.append("}");
816      return sb.toString();
817    }
818  }
819}