001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.filter;
021
022import java.util.Objects;
023import java.util.Random;
024
025import org.apache.hadoop.hbase.Cell;
026import org.apache.yetus.audience.InterfaceAudience;
027import org.apache.hadoop.hbase.exceptions.DeserializationException;
028import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
029
030import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
031
032/**
033 * A filter that includes rows based on a chance.
034 * 
035 */
036@InterfaceAudience.Public
037public class RandomRowFilter extends FilterBase {
038  protected static final Random random = new Random();
039
040  protected float chance;
041  protected boolean filterOutRow;
042
043  /**
044   * Create a new filter with a specified chance for a row to be included.
045   * 
046   * @param chance
047   */
048  public RandomRowFilter(float chance) {
049    this.chance = chance;
050  }
051
052  /**
053   * @return The chance that a row gets included.
054   */
055  public float getChance() {
056    return chance;
057  }
058
059  /**
060   * Set the chance that a row is included.
061   * 
062   * @param chance
063   */
064  public void setChance(float chance) {
065    this.chance = chance;
066  }
067
068  @Override
069  public boolean filterAllRemaining() {
070    return false;
071  }
072
073  @Deprecated
074  @Override
075  public ReturnCode filterKeyValue(final Cell c) {
076    return filterCell(c);
077  }
078
079  @Override
080  public ReturnCode filterCell(final Cell c) {
081    if (filterOutRow) {
082      return ReturnCode.NEXT_ROW;
083    }
084    return ReturnCode.INCLUDE;
085  }
086
087  @Override
088  public boolean filterRow() {
089    return filterOutRow;
090  }
091
092  @Override
093  public boolean hasFilterRow() {
094    return true;
095  }
096
097  @Override
098  public boolean filterRowKey(Cell firstRowCell) {
099    if (chance < 0) {
100      // with a zero chance, the rows is always excluded
101      filterOutRow = true;
102    } else if (chance > 1) {
103      // always included
104      filterOutRow = false;
105    } else {
106      // roll the dice
107      filterOutRow = !(random.nextFloat() < chance);
108    }
109    return filterOutRow;
110  }
111
112  @Override
113  public void reset() {
114    filterOutRow = false;
115  }
116
117  /**
118   * @return The filter serialized using pb
119   */
120  @Override
121  public byte [] toByteArray() {
122    FilterProtos.RandomRowFilter.Builder builder =
123      FilterProtos.RandomRowFilter.newBuilder();
124    builder.setChance(this.chance);
125    return builder.build().toByteArray();
126  }
127
128  /**
129   * @param pbBytes A pb serialized {@link RandomRowFilter} instance
130   * @return An instance of {@link RandomRowFilter} made from <code>bytes</code>
131   * @throws DeserializationException
132   * @see #toByteArray
133   */
134  public static RandomRowFilter parseFrom(final byte [] pbBytes)
135  throws DeserializationException {
136    FilterProtos.RandomRowFilter proto;
137    try {
138      proto = FilterProtos.RandomRowFilter.parseFrom(pbBytes);
139    } catch (InvalidProtocolBufferException e) {
140      throw new DeserializationException(e);
141    }
142    return new RandomRowFilter(proto.getChance());
143  }
144
145  /**
146   * @param o the other filter to compare with
147   * @return true if and only if the fields of the filter that are serialized
148   * are equal to the corresponding fields in other.  Used for testing.
149   */
150  @Override
151  boolean areSerializedFieldsEqual(Filter o) {
152    if (o == this) return true;
153    if (!(o instanceof RandomRowFilter)) return false;
154
155    RandomRowFilter other = (RandomRowFilter)o;
156    return this.getChance() == other.getChance();
157  }
158
159  @Override
160  public boolean equals(Object obj) {
161    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
162  }
163
164  @Override
165  public int hashCode() {
166    return Objects.hash(this.getChance());
167  }
168}