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