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.client;
019
020import java.io.IOException;
021import java.util.List;
022import java.util.Map;
023import java.util.NavigableMap;
024import java.util.TreeMap;
025import java.util.UUID;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.CellBuilder;
028import org.apache.hadoop.hbase.CellBuilderType;
029import org.apache.hadoop.hbase.CellUtil;
030import org.apache.hadoop.hbase.KeyValue;
031import org.apache.hadoop.hbase.io.TimeRange;
032import org.apache.hadoop.hbase.security.access.Permission;
033import org.apache.hadoop.hbase.security.visibility.CellVisibility;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.apache.hadoop.hbase.util.ClassSize;
036import org.apache.yetus.audience.InterfaceAudience;
037
038/**
039 * Used to perform Increment operations on a single row.
040 * <p>
041 * This operation ensures atomicity to readers. Increments are done under a single row lock, so
042 * write operations to a row are synchronized, and readers are guaranteed to see this operation
043 * fully completed.
044 * <p>
045 * To increment columns of a row, instantiate an Increment object with the row to increment. At
046 * least one column to increment must be specified using the
047 * {@link #addColumn(byte[], byte[], long)} method.
048 */
049@InterfaceAudience.Public
050public class Increment extends Mutation {
051  private static final int HEAP_OVERHEAD = ClassSize.REFERENCE + ClassSize.TIMERANGE;
052  private TimeRange tr = TimeRange.allTime();
053
054  /**
055   * Create a Increment operation for the specified row.
056   * <p>
057   * At least one column must be incremented.
058   * @param row row key (we will make a copy of this).
059   */
060  public Increment(byte[] row) {
061    this(row, 0, row.length);
062  }
063
064  /**
065   * Create a Increment operation for the specified row.
066   * <p>
067   * At least one column must be incremented.
068   * @param row row key (we will make a copy of this).
069   */
070  public Increment(final byte[] row, final int offset, final int length) {
071    checkRow(row, offset, length);
072    this.row = Bytes.copy(row, offset, length);
073  }
074
075  /**
076   * Copy constructor
077   * @param incrementToCopy increment to copy
078   */
079  public Increment(Increment incrementToCopy) {
080    super(incrementToCopy);
081    this.tr = incrementToCopy.getTimeRange();
082  }
083
084  /**
085   * Construct the Increment with user defined data. NOTED: 1) all cells in the familyMap must have
086   * the Type.Put 2) the row of each cell must be same with passed row.
087   * @param row       row. CAN'T be null
088   * @param ts        timestamp
089   * @param familyMap the map to collect all cells internally. CAN'T be null
090   */
091  public Increment(byte[] row, long ts, NavigableMap<byte[], List<Cell>> familyMap) {
092    super(row, ts, familyMap);
093  }
094
095  /**
096   * Add the specified KeyValue to this operation.
097   * @param cell individual Cell
098   * @throws java.io.IOException e
099   */
100  @Override
101  public Increment add(Cell cell) throws IOException {
102    super.add(cell);
103    return this;
104  }
105
106  /**
107   * Increment the column from the specific family with the specified qualifier by the specified
108   * amount.
109   * <p>
110   * Overrides previous calls to addColumn for this family and qualifier.
111   * @param family    family name
112   * @param qualifier column qualifier
113   * @param amount    amount to increment by
114   * @return the Increment object
115   */
116  public Increment addColumn(byte[] family, byte[] qualifier, long amount) {
117    if (family == null) {
118      throw new IllegalArgumentException("family cannot be null");
119    }
120    List<Cell> list = getCellList(family);
121    KeyValue kv = createPutKeyValue(family, qualifier, ts, Bytes.toBytes(amount));
122    list.add(kv);
123    return this;
124  }
125
126  /**
127   * Gets the TimeRange used for this increment.
128   */
129  public TimeRange getTimeRange() {
130    return this.tr;
131  }
132
133  /**
134   * Sets the TimeRange to be used on the Get for this increment.
135   * <p>
136   * This is useful for when you have counters that only last for specific periods of time (ie.
137   * counters that are partitioned by time). By setting the range of valid times for this increment,
138   * you can potentially gain some performance with a more optimal Get operation. Be careful adding
139   * the time range to this class as you will update the old cell if the time range doesn't include
140   * the latest cells.
141   * <p>
142   * This range is used as [minStamp, maxStamp).
143   * @param minStamp minimum timestamp value, inclusive
144   * @param maxStamp maximum timestamp value, exclusive
145   * @throws IOException if invalid time range
146   */
147  public Increment setTimeRange(long minStamp, long maxStamp) throws IOException {
148    tr = TimeRange.between(minStamp, maxStamp);
149    return this;
150  }
151
152  @Override
153  public Increment setTimestamp(long timestamp) {
154    super.setTimestamp(timestamp);
155    return this;
156  }
157
158  /**
159   * @param returnResults True (default) if the increment operation should return the results. A
160   *                      client that is not interested in the result can save network bandwidth
161   *                      setting this to false.
162   */
163  @Override
164  public Increment setReturnResults(boolean returnResults) {
165    super.setReturnResults(returnResults);
166    return this;
167  }
168
169  /** Returns current setting for returnResults */
170  // This method makes public the superclasses's protected method.
171  @Override
172  public boolean isReturnResults() {
173    return super.isReturnResults();
174  }
175
176  /**
177   * Method for retrieving the number of families to increment from
178   * @return number of families
179   */
180  @Override
181  public int numFamilies() {
182    return this.familyMap.size();
183  }
184
185  /**
186   * Method for checking if any families have been inserted into this Increment
187   * @return true if familyMap is non empty false otherwise
188   */
189  public boolean hasFamilies() {
190    return !this.familyMap.isEmpty();
191  }
192
193  /**
194   * Before 0.95, when you called Increment#getFamilyMap(), you got back a map of families to a list
195   * of Longs. Now, {@link #getFamilyCellMap()} returns families by list of Cells. This method has
196   * been added so you can have the old behavior.
197   * @return Map of families to a Map of qualifiers and their Long increments.
198   * @since 0.95.0
199   */
200  public Map<byte[], NavigableMap<byte[], Long>> getFamilyMapOfLongs() {
201    NavigableMap<byte[], List<Cell>> map = super.getFamilyCellMap();
202    Map<byte[], NavigableMap<byte[], Long>> results = new TreeMap<>(Bytes.BYTES_COMPARATOR);
203    for (Map.Entry<byte[], List<Cell>> entry : map.entrySet()) {
204      NavigableMap<byte[], Long> longs = new TreeMap<>(Bytes.BYTES_COMPARATOR);
205      for (Cell cell : entry.getValue()) {
206        longs.put(CellUtil.cloneQualifier(cell),
207          Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
208      }
209      results.put(entry.getKey(), longs);
210    }
211    return results;
212  }
213
214  /**
215   *   */
216  @Override
217  public String toString() {
218    StringBuilder sb = new StringBuilder();
219    sb.append("row=");
220    sb.append(Bytes.toStringBinary(this.row));
221    if (this.familyMap.isEmpty()) {
222      sb.append(", no columns set to be incremented");
223      return sb.toString();
224    }
225    sb.append(", families=");
226    boolean moreThanOne = false;
227    for (Map.Entry<byte[], List<Cell>> entry : this.familyMap.entrySet()) {
228      if (moreThanOne) {
229        sb.append("), ");
230      } else {
231        moreThanOne = true;
232        sb.append("{");
233      }
234      sb.append("(family=");
235      sb.append(Bytes.toString(entry.getKey()));
236      sb.append(", columns=");
237      if (entry.getValue() == null) {
238        sb.append("NONE");
239      } else {
240        sb.append("{");
241        boolean moreThanOneB = false;
242        for (Cell cell : entry.getValue()) {
243          if (moreThanOneB) {
244            sb.append(", ");
245          } else {
246            moreThanOneB = true;
247          }
248          sb.append(CellUtil.getCellKeyAsString(cell) + "+="
249            + Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
250        }
251        sb.append("}");
252      }
253    }
254    sb.append("}");
255    return sb.toString();
256  }
257
258  @Override
259  protected long extraHeapSize() {
260    return HEAP_OVERHEAD;
261  }
262
263  @Override
264  public Increment setAttribute(String name, byte[] value) {
265    return (Increment) super.setAttribute(name, value);
266  }
267
268  @Override
269  public Increment setId(String id) {
270    return (Increment) super.setId(id);
271  }
272
273  @Override
274  public Increment setDurability(Durability d) {
275    return (Increment) super.setDurability(d);
276  }
277
278  @Override
279  public Increment setClusterIds(List<UUID> clusterIds) {
280    return (Increment) super.setClusterIds(clusterIds);
281  }
282
283  @Override
284  public Increment setCellVisibility(CellVisibility expression) {
285    return (Increment) super.setCellVisibility(expression);
286  }
287
288  @Override
289  public Increment setACL(String user, Permission perms) {
290    return (Increment) super.setACL(user, perms);
291  }
292
293  @Override
294  public Increment setACL(Map<String, Permission> perms) {
295    return (Increment) super.setACL(perms);
296  }
297
298  @Override
299  public Increment setTTL(long ttl) {
300    return (Increment) super.setTTL(ttl);
301  }
302
303  @Override
304  public Increment setPriority(int priority) {
305    return (Increment) super.setPriority(priority);
306  }
307
308  @Override
309  public CellBuilder getCellBuilder(CellBuilderType type) {
310    return getCellBuilder(type, Cell.Type.Put);
311  }
312}