1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.log.entry; 9 10 import java.lang.reflect.Constructor; 11 import java.nio.ByteBuffer; 12 import java.util.Arrays; 13 14 import com.sleepycat.je.DatabaseEntry; 15 import com.sleepycat.je.EnvironmentFailureException; 16 import com.sleepycat.je.dbi.DatabaseId; 17 import com.sleepycat.je.dbi.DatabaseImpl; 18 import com.sleepycat.je.dbi.DupKeyData; 19 import com.sleepycat.je.dbi.EnvironmentImpl; 20 import com.sleepycat.je.log.LogEntryHeader; 21 import com.sleepycat.je.log.LogEntryType; 22 import com.sleepycat.je.log.LogUtils; 23 import com.sleepycat.je.tree.LN; 24 import com.sleepycat.je.tree.VersionedLN; 25 import com.sleepycat.je.txn.Txn; 26 import com.sleepycat.je.utilint.DbLsn; 27 import com.sleepycat.je.utilint.VLSN; 28 29 /** 30 * LNLogEntry embodies all LN log entries. 31 * On disk, an LN log entry contains (pre version 6) 32 * <pre> 33 * LN 34 * databaseid 35 * key 36 * abortLsn -- if transactional 37 * abortKnownDeleted -- if transactional 38 * txn -- if transactional 39 * 40 * (version 6) 41 * databaseid 42 * abortLsn -- if transactional 43 * abortKnownDeleted -- if transactional 44 * txn -- if transactional 45 * LN 46 * key 47 * </pre> 48 * Before version 6, a non-full-item read of a log entry only retrieved 49 * the node ID. After version 6, the database id, transaction id and node ID 50 * are all available. 51 */ 52 public class LNLogEntry<T extends LN> extends BaseReplicableEntry<T> { 53 private static final byte ABORT_KNOWN_DELETED_MASK = (byte) 1; 54 55 /** 56 * Used for computing the minimum log space used by an LNLogEntry. 57 */ 58 public static final int MIN_LOG_SIZE = 1 + // DatabaseId 59 1 + // LN with zero-length data 60 LogEntryHeader.MIN_HEADER_SIZE; 61 62 /** 63 * The log version of the most recent format change for this entry, 64 * including any changes to the format of the underlying LN and other 65 * loggables. 66 * 67 * @see #getLastFormatChange 68 */ 69 public static final int LAST_FORMAT_CHANGE = 8; 70 71 /* 72 * Persistent fields in an LN entry. 73 */ 74 private LN ln; 75 private DatabaseId dbId; 76 private byte[] key; 77 private long abortLsn = DbLsn.NULL_LSN; 78 private boolean abortKnownDeleted; 79 private Txn txn; // conditional 80 81 /* Transient field for duplicates conversion and user key/data methods. */ 82 enum DupStatus { UNKNOWN, NEED_CONVERSION, DUP_DB, NOT_DUP_DB } 83 private DupStatus dupStatus; 84 85 /* For construction of VersionedLN, when VLSN is preserved. */ 86 private final Constructor<VersionedLN> versionedLNConstructor; 87 88 /** 89 * Creates an instance to read an entry. 90 * 91 * @param <T> the type of the contained LN 92 * @param cls the class of the contained LN 93 * @return the log entry 94 */ create(final Class<T> cls)95 public static <T extends LN> LNLogEntry<T> create(final Class<T> cls) { 96 return new LNLogEntry<T>(cls); 97 } 98 99 /* Constructor to read an entry. */ LNLogEntry(final Class<T> cls)100 LNLogEntry(final Class<T> cls) { 101 super(cls); 102 if (cls == LN.class) { 103 versionedLNConstructor = getNoArgsConstructor(VersionedLN.class); 104 } else { 105 versionedLNConstructor = null; 106 } 107 } 108 109 /* Constructor to write an entry. */ LNLogEntry(LogEntryType entryType, T ln, DatabaseId dbId, byte[] key, long abortLsn, boolean abortKnownDeleted, Txn txn)110 public LNLogEntry(LogEntryType entryType, 111 T ln, 112 DatabaseId dbId, 113 byte[] key, 114 long abortLsn, 115 boolean abortKnownDeleted, 116 Txn txn) { 117 setLogType(entryType); 118 this.ln = ln; 119 this.dbId = dbId; 120 this.key = key; 121 this.abortLsn = abortLsn; 122 this.abortKnownDeleted = abortKnownDeleted; 123 this.txn = txn; 124 versionedLNConstructor = null; 125 126 /* A txn should only be provided for transactional entry types. */ 127 assert(entryType.isTransactional() == (txn != null)); 128 } 129 130 @Override readEntry(EnvironmentImpl envImpl, LogEntryHeader header, ByteBuffer entryBuffer)131 public void readEntry(EnvironmentImpl envImpl, 132 LogEntryHeader header, 133 ByteBuffer entryBuffer) { 134 135 /* Subclasses must call readBaseLNEntry. */ 136 assert getClass() == LNLogEntry.class; 137 138 /* 139 * Prior to version 8, the optimization to omit the key size was 140 * mistakenly not applied to internal LN types such as FileSummaryLN 141 * and MapLN, and was only applied to user LN types. The optimization 142 * should be applicable whenever LNLogEntry is not subclassed to add 143 * additional fields. [#18055] 144 */ 145 final boolean keyIsLastSerializedField = 146 header.getVersion() >= 8 || entryType.isUserLNType(); 147 148 readBaseLNEntry(envImpl, header, entryBuffer, 149 keyIsLastSerializedField); 150 } 151 152 /** 153 * Method shared by LNLogEntry subclasses. 154 * 155 * @param keyIsLastSerializedField specifies whether the key length can be 156 * omitted because the key is the last field. This should be false when 157 * an LNLogEntry subclass adds fields to the serialized format. 158 */ readBaseLNEntry(EnvironmentImpl envImpl, LogEntryHeader header, ByteBuffer entryBuffer, boolean keyIsLastSerializedField)159 final void readBaseLNEntry(EnvironmentImpl envImpl, 160 LogEntryHeader header, 161 ByteBuffer entryBuffer, 162 boolean keyIsLastSerializedField) { 163 164 int logVersion = header.getVersion(); 165 boolean unpacked = (logVersion < 6); 166 int recStartPosition = entryBuffer.position(); 167 168 /* 169 * For log version 6 and above we store the key last so that we can 170 * avoid storing the key size. Instead, we derive it from the LN size 171 * and the total entry size. The DatabaseId is also packed. 172 */ 173 if (unpacked) { 174 /* LN is first for log versions prior to 6. */ 175 ln = newLNInstance(envImpl); 176 ln.readFromLog(entryBuffer, logVersion); 177 } 178 179 /* DatabaseImpl Id. */ 180 dbId = new DatabaseId(); 181 dbId.readFromLog(entryBuffer, logVersion); 182 183 /* Key. */ 184 if (unpacked) { 185 key = LogUtils.readByteArray(entryBuffer, true/*unpacked*/); 186 } else { 187 /* Read later. */ 188 } 189 190 if (entryType.isTransactional()) { 191 192 /* 193 * AbortLsn. If it was a marker LSN that was used to fill in a 194 * create, mark it null. 195 */ 196 abortLsn = LogUtils.readLong(entryBuffer, unpacked); 197 if (DbLsn.getFileNumber(abortLsn) == 198 DbLsn.getFileNumber(DbLsn.NULL_LSN)) { 199 abortLsn = DbLsn.NULL_LSN; 200 } 201 202 abortKnownDeleted = 203 ((entryBuffer.get() & ABORT_KNOWN_DELETED_MASK) != 0) ? 204 true : false; 205 206 /* Locker. */ 207 txn = new Txn(); 208 txn.readFromLog(entryBuffer, logVersion); 209 } 210 211 if (!unpacked) { 212 /* LN is next for log version 6 and above. */ 213 ln = newLNInstance(envImpl); 214 ln.readFromLog(entryBuffer, logVersion); 215 final int keySize; 216 if (keyIsLastSerializedField) { 217 final int bytesWritten = 218 entryBuffer.position() - recStartPosition; 219 keySize = header.getItemSize() - bytesWritten; 220 } else { 221 keySize = LogUtils.readPackedInt(entryBuffer); 222 } 223 key = LogUtils.readBytesNoLength(entryBuffer, keySize); 224 } 225 226 /* Save transient fields after read. */ 227 setLNTransientFields(ln, header.getVLSN()); 228 229 /* Dup conversion will be done by postFetchInit. */ 230 dupStatus = 231 (logVersion < 8) ? DupStatus.NEED_CONVERSION : DupStatus.UNKNOWN; 232 } 233 234 /** 235 * newLNInstance usually returns exactly the type of LN of the type that 236 * was contained in in the log. For example, if a LNLogEntry holds a MapLN, 237 * newLNInstance will return that MapLN. There is one extra possibility for 238 * vanilla (data record) LNs. In that case, this method may either return a 239 * LN or a generated type, the VersionedLN, which adds the vlsn information 240 * from the log header to the LN object. 241 */ newLNInstance(EnvironmentImpl envImpl)242 LN newLNInstance(EnvironmentImpl envImpl) { 243 if (versionedLNConstructor != null && envImpl.getPreserveVLSN()) { 244 return newInstanceOfType(versionedLNConstructor); 245 } 246 return newInstanceOfType(); 247 } 248 249 @Override dumpEntry(StringBuilder sb, boolean verbose)250 public StringBuilder dumpEntry(StringBuilder sb, boolean verbose) { 251 ln.dumpLog(sb, verbose); 252 dbId.dumpLog(sb, verbose); 253 ln.dumpKey(sb, key); 254 if (entryType.isTransactional()) { 255 if (abortLsn != DbLsn.NULL_LSN) { 256 sb.append(DbLsn.toString(abortLsn)); 257 } 258 sb.append("<knownDeleted val=\""); 259 sb.append(abortKnownDeleted ? "true" : "false"); 260 sb.append("\"/>"); 261 txn.dumpLog(sb, verbose); 262 } 263 return sb; 264 } 265 266 @Override dumpRep(StringBuilder sb)267 public void dumpRep(StringBuilder sb) { 268 if (entryType.isTransactional()) { 269 sb.append(" txn=").append(txn.getId()); 270 } 271 } 272 273 @Override getMainItem()274 public LN getMainItem() { 275 return ln; 276 } 277 278 @Override getTransactionId()279 public long getTransactionId() { 280 if (entryType.isTransactional()) { 281 return txn.getId(); 282 } 283 return 0; 284 } 285 286 /* 287 * Writing support. 288 */ 289 290 @Override getLastFormatChange()291 public int getLastFormatChange() { 292 return LAST_FORMAT_CHANGE; 293 } 294 295 @Override getSize()296 public int getSize() { 297 298 /* Subclasses must call getBaseLNEntrySize. */ 299 assert getClass() == LNLogEntry.class; 300 301 return getBaseLNEntrySize(true /*keyIsLastSerializedField*/); 302 } 303 304 /** 305 * Method shared by LNLogEntry subclasses. 306 * 307 * @param keyIsLastSerializedField specifies whether the key length can be 308 * omitted because the key is the last field. This should be false when 309 * an LNLogEntry subclass adds fields to the serialized format. 310 */ getBaseLNEntrySize(boolean keyIsLastSerializedField)311 final int getBaseLNEntrySize(boolean keyIsLastSerializedField) { 312 int len = key.length; 313 int size = ln.getLogSize() + 314 dbId.getLogSize() + 315 len; 316 if (!keyIsLastSerializedField) { 317 size += LogUtils.getPackedIntLogSize(len); 318 } 319 if (entryType.isTransactional()) { 320 size += LogUtils.getPackedLongLogSize(abortLsn); 321 size++; // abortKnownDeleted 322 size += txn.getLogSize(); 323 } 324 return size; 325 } 326 327 @Override writeEntry(final ByteBuffer destBuffer, final int logVersion)328 public void writeEntry(final ByteBuffer destBuffer, final int logVersion) { 329 330 /* Subclasses must call writeBaseLNEntry. */ 331 assert getClass() == LNLogEntry.class; 332 333 writeBaseLNEntry(destBuffer, 334 true /*keyIsLastSerializedField*/, 335 logVersion); 336 } 337 338 /** 339 * Method shared by LNLogEntry subclasses. 340 * 341 * @param keyIsLastSerializedField specifies whether the key length can be 342 * omitted because the key is the last field. This should be false when 343 * an LNLogEntry subclass adds fields to the serialized format. 344 */ writeBaseLNEntry(final ByteBuffer destBuffer, final boolean keyIsLastSerializedField, final int logVersion)345 final void writeBaseLNEntry(final ByteBuffer destBuffer, 346 final boolean keyIsLastSerializedField, 347 final int logVersion) { 348 checkCurrentVersion(this, logVersion); 349 assert ln.getLastFormatChange() <= LAST_FORMAT_CHANGE && 350 dbId.getLastFormatChange() <= LAST_FORMAT_CHANGE 351 : "Format of loggable newer than format of entry"; 352 353 dbId.writeToLog(destBuffer, logVersion); 354 355 if (entryType.isTransactional()) { 356 LogUtils.writePackedLong(destBuffer, abortLsn); 357 byte aKD = 0; 358 if (abortKnownDeleted) { 359 aKD |= ABORT_KNOWN_DELETED_MASK; 360 } 361 destBuffer.put(aKD); 362 assert txn.getLastFormatChange() <= LAST_FORMAT_CHANGE 363 : "Format of loggable newer than format of entry"; 364 txn.writeToLog(destBuffer, logVersion); 365 } 366 367 ln.writeToLog(destBuffer, logVersion); 368 if (!keyIsLastSerializedField) { 369 LogUtils.writePackedInt(destBuffer, key.length); 370 } 371 LogUtils.writeBytesNoLength(destBuffer, key); 372 } 373 374 /** 375 * An LN has two transient fields that are derived from its parent log 376 * entry: last logged size and VLSN sequence. 377 */ setLNTransientFields(LN ln, VLSN vlsn)378 private void setLNTransientFields(LN ln, VLSN vlsn) { 379 if (vlsn != null) { 380 ln.setVLSNSequence(vlsn.getSequence()); 381 } 382 } 383 384 @Override isImmediatelyObsolete(DatabaseImpl dbImpl)385 public boolean isImmediatelyObsolete(DatabaseImpl dbImpl) { 386 return ln.isDeleted() || dbImpl.isLNImmediatelyObsolete(); 387 } 388 389 @Override isDeleted()390 public boolean isDeleted() { 391 return ln.isDeleted(); 392 } 393 394 /** 395 * For LN entries, we need to record the latest LSN for that node with the 396 * owning transaction, within the protection of the log latch. This is a 397 * callback for the log manager to do that recording. 398 */ 399 @Override postLogWork(LogEntryHeader header, long justLoggedLsn, VLSN vlsn)400 public void postLogWork(LogEntryHeader header, 401 long justLoggedLsn, 402 VLSN vlsn) { 403 if (entryType.isTransactional()) { 404 txn.addLogInfo(justLoggedLsn); 405 } 406 /* Save transient fields after write. */ 407 setLNTransientFields(ln, vlsn); 408 } 409 410 @Override postFetchInit(DatabaseImpl dbImpl)411 public void postFetchInit(DatabaseImpl dbImpl) { 412 postFetchInit(dbImpl.getSortedDuplicates()); 413 } 414 415 /** 416 * Converts the key/data for old format LNs in a duplicates DB. 417 * 418 * This method MUST be called before calling any of the following methods: 419 * getLN 420 * getKey 421 * getUserKeyData 422 */ postFetchInit(boolean isDupDb)423 public void postFetchInit(boolean isDupDb) { 424 425 final boolean needConversion = 426 (dupStatus == DupStatus.NEED_CONVERSION); 427 428 dupStatus = isDupDb ? DupStatus.DUP_DB : DupStatus.NOT_DUP_DB; 429 430 /* Do not convert more than once. */ 431 if (!needConversion) { 432 return; 433 } 434 435 /* Nothing to convert for non-duplicates DB. */ 436 if (dupStatus == DupStatus.NOT_DUP_DB) { 437 return; 438 } 439 440 key = combineDupKeyData(); 441 } 442 443 /** 444 * Combine old key and old LN's data into a new key, and set the LN's data 445 * to empty. 446 */ combineDupKeyData()447 byte[] combineDupKeyData() { 448 assert !ln.isDeleted(); // DeletedLNLogEntry overrides this method. 449 return DupKeyData.combine(key, ln.setEmpty()); 450 } 451 452 /** 453 * Translates two-part keys in duplicate DBs back to the original user 454 * operation params. postFetchInit must be called before calling this 455 * method. 456 */ getUserKeyData(DatabaseEntry keyParam, DatabaseEntry dataParam)457 public void getUserKeyData(DatabaseEntry keyParam, 458 DatabaseEntry dataParam) { 459 460 requireKnownDupStatus(); 461 462 if (dupStatus == DupStatus.DUP_DB) { 463 DupKeyData.split(new DatabaseEntry(key), keyParam, dataParam); 464 } else { 465 if (keyParam != null) { 466 keyParam.setData(key); 467 } 468 if (dataParam != null) { 469 dataParam.setData(ln.getData()); 470 } 471 } 472 } 473 474 /* 475 * Accessors. 476 */ getLN()477 public LN getLN() { 478 requireKnownDupStatus(); 479 return ln; 480 } 481 getKey()482 public byte[] getKey() { 483 requireKnownDupStatus(); 484 return key; 485 } 486 requireKnownDupStatus()487 private void requireKnownDupStatus() { 488 if (dupStatus != DupStatus.DUP_DB && 489 dupStatus != DupStatus.NOT_DUP_DB) { 490 throw EnvironmentFailureException.unexpectedState 491 ("postFetchInit was not called"); 492 } 493 } 494 495 /** 496 * This method is only used when the converted length is not needed, for 497 * example by StatsFileReader. 498 */ getUnconvertedDataLength()499 public int getUnconvertedDataLength() { 500 return ln.getData().length; 501 } 502 503 /** 504 * This method is only used when the converted length is not needed, for 505 * example by StatsFileReader. 506 */ getUnconvertedKeyLength()507 public int getUnconvertedKeyLength() { 508 return key.length; 509 } 510 511 @Override getDbId()512 public DatabaseId getDbId() { 513 return dbId; 514 } 515 getAbortLsn()516 public long getAbortLsn() { 517 return abortLsn; 518 } 519 getAbortKnownDeleted()520 public boolean getAbortKnownDeleted() { 521 return abortKnownDeleted; 522 } 523 getTxnId()524 public Long getTxnId() { 525 if (entryType.isTransactional()) { 526 return Long.valueOf(txn.getId()); 527 } 528 return null; 529 } 530 getUserTxn()531 public Txn getUserTxn() { 532 if (entryType.isTransactional()) { 533 return txn; 534 } 535 return null; 536 } 537 538 @Override logicalEquals(LogEntry other)539 public boolean logicalEquals(LogEntry other) { 540 if (!(other instanceof LNLogEntry)) { 541 return false; 542 } 543 544 LNLogEntry<?> otherEntry = (LNLogEntry<?>) other; 545 546 if (!dbId.logicalEquals(otherEntry.dbId)) { 547 return false; 548 } 549 550 if (txn != null) { 551 if (!txn.logicalEquals(otherEntry.txn)) { 552 return false; 553 } 554 } else { 555 if (otherEntry.txn != null) { 556 return false; 557 } 558 } 559 560 if (!Arrays.equals(key, otherEntry.key)) { 561 return false; 562 } 563 564 if (!ln.logicalEquals(otherEntry.ln)) { 565 return false; 566 } 567 568 return true; 569 } 570 } 571