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