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.wal;
019
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025import java.util.NavigableMap;
026import java.util.TreeMap;
027import java.util.UUID;
028import org.apache.hadoop.hbase.HBaseInterfaceAudience;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
032import org.apache.hadoop.hbase.regionserver.SequenceId;
033import org.apache.hadoop.hbase.regionserver.wal.CompressionContext;
034import org.apache.hadoop.hbase.regionserver.wal.WALCellCodec;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
037import org.apache.yetus.audience.InterfaceAudience;
038
039import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
040
041import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
042import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos;
043import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.FamilyScope;
044import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.ScopeType;
045
046/**
047 * Default implementation of Key for an Entry in the WAL.
048 * For internal use only though Replication needs to have access.
049 *
050 * The log intermingles edits to many tables and rows, so each log entry
051 * identifies the appropriate table and row.  Within a table and row, they're
052 * also sorted.
053 *
054 * <p>Some Transactional edits (START, COMMIT, ABORT) will not have an associated row.
055 *
056 */
057// TODO: Key and WALEdit are never used separately, or in one-to-many relation, for practical
058//       purposes. They need to be merged into WALEntry.
059@InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.REPLICATION})
060public class WALKeyImpl implements WALKey {
061  public static final WALKeyImpl EMPTY_WALKEYIMPL = new WALKeyImpl();
062
063  public MultiVersionConcurrencyControl getMvcc() {
064    return mvcc;
065  }
066
067  /**
068   * Use it to complete mvcc transaction. This WALKeyImpl was part of
069   * (the transaction is started when you call append; see the comment on FSHLog#append). To
070   * complete call
071   * {@link MultiVersionConcurrencyControl#complete(MultiVersionConcurrencyControl.WriteEntry)}
072   * or {@link MultiVersionConcurrencyControl#complete(MultiVersionConcurrencyControl.WriteEntry)}
073   * @return A WriteEntry gotten from local WAL subsystem.
074   * @see #setWriteEntry(MultiVersionConcurrencyControl.WriteEntry)
075   */
076  public MultiVersionConcurrencyControl.WriteEntry getWriteEntry() {
077    return this.writeEntry;
078  }
079
080  public void setWriteEntry(MultiVersionConcurrencyControl.WriteEntry writeEntry) {
081    assert this.writeEntry == null;
082    this.writeEntry = writeEntry;
083    // Set our sequenceid now using WriteEntry.
084    this.sequenceId = writeEntry.getWriteNumber();
085  }
086
087  private byte [] encodedRegionName;
088
089  private TableName tablename;
090
091  /**
092   * SequenceId for this edit. Set post-construction at write-to-WAL time. Until then it is
093   * NO_SEQUENCE_ID. Change it so multiple threads can read it -- e.g. access is synchronized.
094   */
095  private long sequenceId;
096
097  /**
098   * Used during WAL replay; the sequenceId of the edit when it came into the system.
099   */
100  private long origLogSeqNum = 0;
101
102  /** Time at which this edit was written. */
103  private long writeTime;
104
105  /** The first element in the list is the cluster id on which the change has originated */
106  private List<UUID> clusterIds;
107
108  private NavigableMap<byte[], Integer> replicationScope;
109
110  private long nonceGroup = HConstants.NO_NONCE;
111  private long nonce = HConstants.NO_NONCE;
112  private MultiVersionConcurrencyControl mvcc;
113
114  /**
115   * Set in a way visible to multiple threads; e.g. synchronized getter/setters.
116   */
117  private MultiVersionConcurrencyControl.WriteEntry writeEntry;
118
119  private Map<String, byte[]> extendedAttributes;
120
121  public WALKeyImpl() {
122    init(null, null, 0L, HConstants.LATEST_TIMESTAMP,
123        new ArrayList<>(), HConstants.NO_NONCE, HConstants.NO_NONCE, null, null, null);
124  }
125
126  public WALKeyImpl(final NavigableMap<byte[], Integer> replicationScope) {
127    init(null, null, 0L, HConstants.LATEST_TIMESTAMP,
128        new ArrayList<>(), HConstants.NO_NONCE, HConstants.NO_NONCE, null, replicationScope, null);
129  }
130
131  @InterfaceAudience.Private
132  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename,
133                long logSeqNum,
134      final long now, UUID clusterId) {
135    List<UUID> clusterIds = new ArrayList<>(1);
136    clusterIds.add(clusterId);
137    init(encodedRegionName, tablename, logSeqNum, now, clusterIds, HConstants.NO_NONCE,
138      HConstants.NO_NONCE, null, null, null);
139  }
140
141  // TODO: Fix being able to pass in sequenceid.
142  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename, final long now) {
143    init(encodedRegionName,
144        tablename,
145        NO_SEQUENCE_ID,
146        now,
147        EMPTY_UUIDS,
148        HConstants.NO_NONCE,
149        HConstants.NO_NONCE,
150        null, null, null);
151  }
152
153  // TODO: Fix being able to pass in sequenceid.
154  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename, final long now,
155      final NavigableMap<byte[], Integer> replicationScope) {
156    init(encodedRegionName, tablename, NO_SEQUENCE_ID, now, EMPTY_UUIDS, HConstants.NO_NONCE,
157        HConstants.NO_NONCE, null, replicationScope, null);
158  }
159
160  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename, final long now,
161      MultiVersionConcurrencyControl mvcc, final NavigableMap<byte[], Integer> replicationScope) {
162    init(encodedRegionName, tablename, NO_SEQUENCE_ID, now, EMPTY_UUIDS, HConstants.NO_NONCE,
163        HConstants.NO_NONCE, mvcc, replicationScope, null);
164  }
165
166  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename, final long now,
167                    MultiVersionConcurrencyControl mvcc,
168                    final NavigableMap<byte[], Integer> replicationScope,
169                    Map<String, byte[]> extendedAttributes) {
170    init(encodedRegionName, tablename, NO_SEQUENCE_ID, now, EMPTY_UUIDS, HConstants.NO_NONCE,
171        HConstants.NO_NONCE, mvcc, replicationScope, extendedAttributes);
172  }
173
174  public WALKeyImpl(final byte[] encodedRegionName,
175                final TableName tablename,
176                final long now,
177                MultiVersionConcurrencyControl mvcc) {
178    init(encodedRegionName,
179        tablename,
180        NO_SEQUENCE_ID,
181        now,
182        EMPTY_UUIDS,
183        HConstants.NO_NONCE,
184        HConstants.NO_NONCE,
185        mvcc, null, null);
186  }
187
188  /**
189   * Copy constructor that takes in an existing WALKeyImpl plus some extended attributes.
190   * Intended for coprocessors to add annotations to a system-generated WALKey
191   * for persistence to the WAL.
192   * @param key Key to be copied into this new key
193   * @param extendedAttributes Extra attributes to copy into the new key
194   */
195  public WALKeyImpl(WALKeyImpl key,
196                    Map<String, byte[]> extendedAttributes){
197    init(key.getEncodedRegionName(), key.getTableName(), key.getSequenceId(),
198        key.getWriteTime(), key.getClusterIds(), key.getNonceGroup(), key.getNonce(),
199        key.getMvcc(), key.getReplicationScopes(), extendedAttributes);
200
201  }
202
203  /**
204   * Copy constructor that takes in an existing WALKey, the extra WALKeyImpl fields that the
205   * parent interface is missing, plus some extended attributes. Intended
206   * for coprocessors to add annotations to a system-generated WALKey for
207   * persistence to the WAL.
208   */
209  public WALKeyImpl(WALKey key,
210                    List<UUID> clusterIds,
211                    MultiVersionConcurrencyControl mvcc,
212                    final NavigableMap<byte[], Integer> replicationScopes,
213                    Map<String, byte[]> extendedAttributes){
214    init(key.getEncodedRegionName(), key.getTableName(), key.getSequenceId(),
215        key.getWriteTime(), clusterIds, key.getNonceGroup(), key.getNonce(),
216        mvcc, replicationScopes, extendedAttributes);
217
218  }
219  /**
220   * Create the log key for writing to somewhere.
221   * We maintain the tablename mainly for debugging purposes.
222   * A regionName is always a sub-table object.
223   * <p>Used by log splitting and snapshots.
224   *
225   * @param encodedRegionName Encoded name of the region as returned by
226   *                         <code>HRegionInfo#getEncodedNameAsBytes()</code>.
227   * @param tablename         - name of table
228   * @param logSeqNum         - log sequence number
229   * @param now               Time at which this edit was written.
230   * @param clusterIds        the clusters that have consumed the change(used in Replication)
231   * @param nonceGroup        the nonceGroup
232   * @param nonce             the nonce
233   * @param mvcc              the mvcc associate the WALKeyImpl
234   * @param replicationScope  the non-default replication scope
235   *                          associated with the region's column families
236   */
237  // TODO: Fix being able to pass in sequenceid.
238  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename, long logSeqNum,
239      final long now, List<UUID> clusterIds, long nonceGroup, long nonce,
240      MultiVersionConcurrencyControl mvcc, final NavigableMap<byte[], Integer> replicationScope) {
241    init(encodedRegionName, tablename, logSeqNum, now, clusterIds, nonceGroup, nonce, mvcc,
242        replicationScope, null);
243  }
244
245  /**
246   * Create the log key for writing to somewhere.
247   * We maintain the tablename mainly for debugging purposes.
248   * A regionName is always a sub-table object.
249   * <p>Used by log splitting and snapshots.
250   *
251   * @param encodedRegionName Encoded name of the region as returned by
252   *                          <code>HRegionInfo#getEncodedNameAsBytes()</code>.
253   * @param tablename         - name of table
254   * @param logSeqNum         - log sequence number
255   * @param now               Time at which this edit was written.
256   * @param clusterIds        the clusters that have consumed the change(used in Replication)
257   */
258  // TODO: Fix being able to pass in sequenceid.
259  public WALKeyImpl(final byte[] encodedRegionName,
260                final TableName tablename,
261                long logSeqNum,
262                final long now,
263                List<UUID> clusterIds,
264                long nonceGroup,
265                long nonce,
266                MultiVersionConcurrencyControl mvcc) {
267    init(encodedRegionName, tablename, logSeqNum, now, clusterIds, nonceGroup,
268        nonce, mvcc, null, null);
269  }
270
271  /**
272   * Create the log key for writing to somewhere.
273   * We maintain the tablename mainly for debugging purposes.
274   * A regionName is always a sub-table object.
275   *
276   * @param encodedRegionName Encoded name of the region as returned by
277   *                          <code>HRegionInfo#getEncodedNameAsBytes()</code>.
278   * @param tablename         the tablename
279   * @param now               Time at which this edit was written.
280   * @param clusterIds        the clusters that have consumed the change(used in Replication)
281   * @param nonceGroup
282   * @param nonce
283   * @param mvcc mvcc control used to generate sequence numbers and control read/write points
284   */
285  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename,
286                final long now, List<UUID> clusterIds, long nonceGroup,
287                final long nonce, final MultiVersionConcurrencyControl mvcc) {
288    init(encodedRegionName, tablename, NO_SEQUENCE_ID, now, clusterIds, nonceGroup, nonce, mvcc,
289        null, null);
290  }
291
292  /**
293   * Create the log key for writing to somewhere.
294   * We maintain the tablename mainly for debugging purposes.
295   * A regionName is always a sub-table object.
296   *
297   * @param encodedRegionName Encoded name of the region as returned by
298   *                          <code>HRegionInfo#getEncodedNameAsBytes()</code>.
299   * @param tablename
300   * @param now               Time at which this edit was written.
301   * @param clusterIds        the clusters that have consumed the change(used in Replication)
302   * @param nonceGroup        the nonceGroup
303   * @param nonce             the nonce
304   * @param mvcc mvcc control used to generate sequence numbers and control read/write points
305   * @param replicationScope  the non-default replication scope of the column families
306   */
307  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename,
308                final long now, List<UUID> clusterIds, long nonceGroup,
309                final long nonce, final MultiVersionConcurrencyControl mvcc,
310                NavigableMap<byte[], Integer> replicationScope) {
311    init(encodedRegionName, tablename, NO_SEQUENCE_ID, now, clusterIds, nonceGroup, nonce, mvcc,
312        replicationScope, null);
313  }
314
315  /**
316   * Create the log key for writing to somewhere.
317   * We maintain the tablename mainly for debugging purposes.
318   * A regionName is always a sub-table object.
319   *
320   * @param encodedRegionName Encoded name of the region as returned by
321   *                          <code>HRegionInfo#getEncodedNameAsBytes()</code>.
322   * @param tablename
323   * @param logSeqNum
324   * @param nonceGroup
325   * @param nonce
326   */
327  // TODO: Fix being able to pass in sequenceid.
328  public WALKeyImpl(final byte[] encodedRegionName,
329                final TableName tablename,
330                long logSeqNum,
331                long nonceGroup,
332                long nonce,
333                final MultiVersionConcurrencyControl mvcc) {
334    init(encodedRegionName,
335        tablename,
336        logSeqNum,
337        EnvironmentEdgeManager.currentTime(),
338        EMPTY_UUIDS,
339        nonceGroup,
340        nonce,
341        mvcc, null, null);
342  }
343
344  public WALKeyImpl(final byte[] encodedRegionName, final TableName tablename,
345                    final long now, List<UUID> clusterIds, long nonceGroup,
346                    final long nonce, final MultiVersionConcurrencyControl mvcc,
347                    NavigableMap<byte[], Integer> replicationScope,
348                    Map<String, byte[]> extendedAttributes){
349    init(encodedRegionName,
350        tablename,
351        NO_SEQUENCE_ID,
352        now,
353        clusterIds,
354        nonceGroup,
355        nonce,
356        mvcc, replicationScope, extendedAttributes);
357  }
358
359  @InterfaceAudience.Private
360  protected void init(final byte[] encodedRegionName,
361                      final TableName tablename,
362                      long logSeqNum,
363                      final long now,
364                      List<UUID> clusterIds,
365                      long nonceGroup,
366                      long nonce,
367                      MultiVersionConcurrencyControl mvcc,
368                      NavigableMap<byte[], Integer> replicationScope,
369                      Map<String, byte[]> extendedAttributes) {
370    this.sequenceId = logSeqNum;
371    this.writeTime = now;
372    this.clusterIds = clusterIds;
373    this.encodedRegionName = encodedRegionName;
374    this.tablename = tablename;
375    this.nonceGroup = nonceGroup;
376    this.nonce = nonce;
377    this.mvcc = mvcc;
378    if (logSeqNum != NO_SEQUENCE_ID) {
379      setSequenceId(logSeqNum);
380    }
381    this.replicationScope = replicationScope;
382    this.extendedAttributes = extendedAttributes;
383  }
384
385  // For deserialization. DO NOT USE. See setWriteEntry below.
386  @InterfaceAudience.Private
387  protected void setSequenceId(long sequenceId) {
388    this.sequenceId = sequenceId;
389  }
390
391  /**
392   * @param compressionContext Compression context to use
393   * @deprecated deparcated since hbase 2.1.0
394   */
395  @Deprecated
396  public void setCompressionContext(CompressionContext compressionContext) {
397    //do nothing
398  }
399
400  /** @return encoded region name */
401  @Override
402  public byte [] getEncodedRegionName() {
403    return encodedRegionName;
404  }
405
406  /** @return table name */
407  @Override
408  public TableName getTableName() {
409    return tablename;
410  }
411
412  /** @return log sequence number
413   * @deprecated Use {@link #getSequenceId()}
414   */
415  @Deprecated
416  public long getLogSeqNum() {
417    return getSequenceId();
418  }
419
420  /**
421   * Used to set original sequenceId for WALKeyImpl during WAL replay
422   */
423  public void setOrigLogSeqNum(final long sequenceId) {
424    this.origLogSeqNum = sequenceId;
425  }
426
427  /**
428   * Return a positive long if current WALKeyImpl is created from a replay edit; a replay edit is an
429   * edit that came in when replaying WALs of a crashed server.
430   * @return original sequence number of the WALEdit
431   */
432  @Override
433  public long getOrigLogSeqNum() {
434    return this.origLogSeqNum;
435  }
436
437  /**
438   * SequenceId is only available post WAL-assign. Calls before this will get you a
439   * {@link SequenceId#NO_SEQUENCE_ID}. See the comment on FSHLog#append and #getWriteNumber in this
440   * method for more on when this sequenceId comes available.
441   * @return long the new assigned sequence number
442   */
443  @Override
444  public long getSequenceId() {
445    return this.sequenceId;
446  }
447
448  /**
449   * @return the write time
450   */
451  @Override
452  public long getWriteTime() {
453    return this.writeTime;
454  }
455
456  public NavigableMap<byte[], Integer> getReplicationScopes() {
457    return replicationScope;
458  }
459
460  /** @return The nonce group */
461  @Override
462  public long getNonceGroup() {
463    return nonceGroup;
464  }
465
466  /** @return The nonce */
467  @Override
468  public long getNonce() {
469    return nonce;
470  }
471
472  private void setReplicationScope(NavigableMap<byte[], Integer> replicationScope) {
473    this.replicationScope = replicationScope;
474  }
475
476  public void clearReplicationScope() {
477    setReplicationScope(null);
478  }
479
480  /**
481   * Marks that the cluster with the given clusterId has consumed the change
482   */
483  public void addClusterId(UUID clusterId) {
484    if (!clusterIds.contains(clusterId)) {
485      clusterIds.add(clusterId);
486    }
487  }
488
489  /**
490   * @return the set of cluster Ids that have consumed the change
491   */
492  public List<UUID> getClusterIds() {
493    return clusterIds;
494  }
495
496  /**
497   * @return the cluster id on which the change has originated. It there is no such cluster, it
498   *         returns DEFAULT_CLUSTER_ID (cases where replication is not enabled)
499   */
500  @Override
501  public UUID getOriginatingClusterId(){
502    return clusterIds.isEmpty()? HConstants.DEFAULT_CLUSTER_ID: clusterIds.get(0);
503  }
504
505  @Override
506  public void addExtendedAttribute(String attributeKey, byte[] attributeValue){
507    if (extendedAttributes == null){
508      extendedAttributes = new HashMap<String, byte[]>();
509    }
510    extendedAttributes.put(attributeKey, attributeValue);
511  }
512
513  @Override
514  public byte[] getExtendedAttribute(String attributeKey){
515    return extendedAttributes != null ? extendedAttributes.get(attributeKey) : null;
516  }
517
518  @Override
519  public Map<String, byte[]> getExtendedAttributes(){
520    return extendedAttributes != null ? new HashMap<String, byte[]>(extendedAttributes) :
521        new HashMap<String, byte[]>();
522  }
523
524  @Override
525  public String toString() {
526    return tablename + "/" + Bytes.toString(encodedRegionName) + "/" + sequenceId;
527  }
528
529  @Override
530  public boolean equals(Object obj) {
531    if (this == obj) {
532      return true;
533    }
534    if (obj == null || getClass() != obj.getClass()) {
535      return false;
536    }
537    return compareTo((WALKey)obj) == 0;
538  }
539
540  @Override
541  public int hashCode() {
542    int result = Bytes.hashCode(this.encodedRegionName);
543    result = (int) (result ^ getSequenceId());
544    result = (int) (result ^ this.writeTime);
545    return result;
546  }
547
548  @Override
549  public int compareTo(WALKey o) {
550    int result = Bytes.compareTo(this.encodedRegionName, o.getEncodedRegionName());
551    if (result == 0) {
552      long sid = getSequenceId();
553      long otherSid = o.getSequenceId();
554      if (sid < otherSid) {
555        result = -1;
556      } else if (sid  > otherSid) {
557        result = 1;
558      }
559      if (result == 0) {
560        if (this.writeTime < o.getWriteTime()) {
561          result = -1;
562        } else if (this.writeTime > o.getWriteTime()) {
563          return 1;
564        }
565      }
566    }
567    // why isn't cluster id accounted for?
568    return result;
569  }
570
571  /**
572   * Drop this instance's tablename byte array and instead
573   * hold a reference to the provided tablename. This is not
574   * meant to be a general purpose setter - it's only used
575   * to collapse references to conserve memory.
576   */
577  void internTableName(TableName tablename) {
578    // We should not use this as a setter - only to swap
579    // in a new reference to the same table name.
580    assert tablename.equals(this.tablename);
581    this.tablename = tablename;
582  }
583
584  /**
585   * Drop this instance's region name byte array and instead
586   * hold a reference to the provided region name. This is not
587   * meant to be a general purpose setter - it's only used
588   * to collapse references to conserve memory.
589   */
590  void internEncodedRegionName(byte []encodedRegionName) {
591    // We should not use this as a setter - only to swap
592    // in a new reference to the same table name.
593    assert Bytes.equals(this.encodedRegionName, encodedRegionName);
594    this.encodedRegionName = encodedRegionName;
595  }
596
597  public WALProtos.WALKey.Builder getBuilder(WALCellCodec.ByteStringCompressor compressor)
598      throws IOException {
599    WALProtos.WALKey.Builder builder = WALProtos.WALKey.newBuilder();
600    builder.setEncodedRegionName(
601      compressor.compress(this.encodedRegionName, CompressionContext.DictionaryIndex.REGION));
602    builder.setTableName(
603      compressor.compress(this.tablename.getName(), CompressionContext.DictionaryIndex.TABLE));
604    builder.setLogSequenceNumber(getSequenceId());
605    builder.setWriteTime(writeTime);
606    if (this.origLogSeqNum > 0) {
607      builder.setOrigSequenceNumber(this.origLogSeqNum);
608    }
609    if (this.nonce != HConstants.NO_NONCE) {
610      builder.setNonce(nonce);
611    }
612    if (this.nonceGroup != HConstants.NO_NONCE) {
613      builder.setNonceGroup(nonceGroup);
614    }
615    HBaseProtos.UUID.Builder uuidBuilder = HBaseProtos.UUID.newBuilder();
616    for (UUID clusterId : clusterIds) {
617      uuidBuilder.setLeastSigBits(clusterId.getLeastSignificantBits());
618      uuidBuilder.setMostSigBits(clusterId.getMostSignificantBits());
619      builder.addClusterIds(uuidBuilder.build());
620    }
621    if (replicationScope != null) {
622      for (Map.Entry<byte[], Integer> e : replicationScope.entrySet()) {
623        ByteString family =
624            compressor.compress(e.getKey(), CompressionContext.DictionaryIndex.FAMILY);
625        builder.addScopes(FamilyScope.newBuilder().setFamily(family)
626            .setScopeType(ScopeType.forNumber(e.getValue())));
627      }
628    }
629    if (extendedAttributes != null){
630      for (Map.Entry<String, byte[]> e : extendedAttributes.entrySet()){
631        WALProtos.Attribute attr = WALProtos.Attribute.newBuilder().
632            setKey(e.getKey()).setValue(compressor.compress(e.getValue(),
633            CompressionContext.DictionaryIndex.TABLE)).build();
634        builder.addExtendedAttributes(attr);
635      }
636    }
637    return builder;
638  }
639
640  public void readFieldsFromPb(WALProtos.WALKey walKey,
641      WALCellCodec.ByteStringUncompressor uncompressor) throws IOException {
642    this.encodedRegionName = uncompressor.uncompress(walKey.getEncodedRegionName(),
643      CompressionContext.DictionaryIndex.REGION);
644    byte[] tablenameBytes =
645        uncompressor.uncompress(walKey.getTableName(), CompressionContext.DictionaryIndex.TABLE);
646    this.tablename = TableName.valueOf(tablenameBytes);
647    clusterIds.clear();
648    for (HBaseProtos.UUID clusterId : walKey.getClusterIdsList()) {
649      clusterIds.add(new UUID(clusterId.getMostSigBits(), clusterId.getLeastSigBits()));
650    }
651    if (walKey.hasNonceGroup()) {
652      this.nonceGroup = walKey.getNonceGroup();
653    }
654    if (walKey.hasNonce()) {
655      this.nonce = walKey.getNonce();
656    }
657    this.replicationScope = null;
658    if (walKey.getScopesCount() > 0) {
659      this.replicationScope = new TreeMap<>(Bytes.BYTES_COMPARATOR);
660      for (FamilyScope scope : walKey.getScopesList()) {
661        byte[] family =
662            uncompressor.uncompress(scope.getFamily(), CompressionContext.DictionaryIndex.FAMILY);
663        this.replicationScope.put(family, scope.getScopeType().getNumber());
664      }
665    }
666    setSequenceId(walKey.getLogSequenceNumber());
667    this.writeTime = walKey.getWriteTime();
668    if (walKey.hasOrigSequenceNumber()) {
669      this.origLogSeqNum = walKey.getOrigSequenceNumber();
670    }
671    if (walKey.getExtendedAttributesCount() > 0){
672      this.extendedAttributes = new HashMap<>(walKey.getExtendedAttributesCount());
673      for (WALProtos.Attribute attr : walKey.getExtendedAttributesList()){
674        byte[] value =
675            uncompressor.uncompress(attr.getValue(), CompressionContext.DictionaryIndex.TABLE);
676        extendedAttributes.put(attr.getKey(), value);
677      }
678    }
679  }
680
681  @Override
682  public long estimatedSerializedSizeOf() {
683    long size = encodedRegionName != null ? encodedRegionName.length : 0;
684    size += tablename != null ? tablename.toBytes().length : 0;
685    if (clusterIds != null) {
686      size += 16 * clusterIds.size();
687    }
688    if (nonceGroup != HConstants.NO_NONCE) {
689      size += Bytes.SIZEOF_LONG; // nonce group
690    }
691    if (nonce != HConstants.NO_NONCE) {
692      size += Bytes.SIZEOF_LONG; // nonce
693    }
694    if (replicationScope != null) {
695      for (Map.Entry<byte[], Integer> scope: replicationScope.entrySet()) {
696        size += scope.getKey().length;
697        size += Bytes.SIZEOF_INT;
698      }
699    }
700    size += Bytes.SIZEOF_LONG; // sequence number
701    size += Bytes.SIZEOF_LONG; // write time
702    if (origLogSeqNum > 0) {
703      size += Bytes.SIZEOF_LONG; // original sequence number
704    }
705    return size;
706  }
707}