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.filter;
019
020import java.util.Objects;
021import java.util.concurrent.ThreadLocalRandom;
022import org.apache.hadoop.hbase.Cell;
023import org.apache.hadoop.hbase.exceptions.DeserializationException;
024import org.apache.yetus.audience.InterfaceAudience;
025
026import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
027
028import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
029
030/**
031 * A filter that includes rows based on a chance.
032 */
033@InterfaceAudience.Public
034public class RandomRowFilter extends FilterBase {
035
036  protected float chance;
037  protected boolean filterOutRow;
038
039  /**
040   * Create a new filter with a specified chance for a row to be included.
041   */
042  public RandomRowFilter(float chance) {
043    this.chance = chance;
044  }
045
046  /** Returns The chance that a row gets included. */
047  public float getChance() {
048    return chance;
049  }
050
051  /**
052   * Set the chance that a row is included.
053   */
054  public void setChance(float chance) {
055    this.chance = chance;
056  }
057
058  @Override
059  public boolean filterAllRemaining() {
060    return false;
061  }
062
063  @Override
064  public ReturnCode filterCell(final Cell c) {
065    if (filterOutRow) {
066      return ReturnCode.NEXT_ROW;
067    }
068    return ReturnCode.INCLUDE;
069  }
070
071  @Override
072  public boolean filterRow() {
073    return filterOutRow;
074  }
075
076  @Override
077  public boolean hasFilterRow() {
078    return true;
079  }
080
081  @Override
082  public boolean filterRowKey(Cell firstRowCell) {
083    if (chance < 0) {
084      // with a zero chance, the rows is always excluded
085      filterOutRow = true;
086    } else if (chance > 1) {
087      // always included
088      filterOutRow = false;
089    } else {
090      // roll the dice
091      filterOutRow = !(ThreadLocalRandom.current().nextFloat() < chance);
092    }
093    return filterOutRow;
094  }
095
096  @Override
097  public void reset() {
098    filterOutRow = false;
099  }
100
101  /** Returns The filter serialized using pb */
102  @Override
103  public byte[] toByteArray() {
104    FilterProtos.RandomRowFilter.Builder builder = FilterProtos.RandomRowFilter.newBuilder();
105    builder.setChance(this.chance);
106    return builder.build().toByteArray();
107  }
108
109  /**
110   * Parse a serialized representation of {@link RandomRowFilter}
111   * @param pbBytes A pb serialized {@link RandomRowFilter} instance
112   * @return An instance of {@link RandomRowFilter} made from <code>bytes</code>
113   * @throws DeserializationException if an error occurred
114   * @see #toByteArray
115   */
116  public static RandomRowFilter parseFrom(final byte[] pbBytes) throws DeserializationException {
117    FilterProtos.RandomRowFilter proto;
118    try {
119      proto = FilterProtos.RandomRowFilter.parseFrom(pbBytes);
120    } catch (InvalidProtocolBufferException e) {
121      throw new DeserializationException(e);
122    }
123    return new RandomRowFilter(proto.getChance());
124  }
125
126  /**
127   * Returns true if and only if the fields of the filter that are serialized are equal to the
128   * corresponding fields in other. Used for testing.
129   */
130  @Override
131  boolean areSerializedFieldsEqual(Filter o) {
132    if (o == this) {
133      return true;
134    }
135    if (!(o instanceof RandomRowFilter)) {
136      return false;
137    }
138    RandomRowFilter other = (RandomRowFilter) o;
139    return this.getChance() == other.getChance();
140  }
141
142  @Override
143  public boolean equals(Object obj) {
144    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
145  }
146
147  @Override
148  public int hashCode() {
149    return Objects.hash(this.getChance());
150  }
151}