예제 #1
0
  /**
   * Create a new LONG_RECORD chain and stores the supplied byte array in the pages of this chain.
   * The chain is written in right-to-left order so that any page having a right pointer points to a
   * valid successor.
   *
   * <p>Each page is written with its own timestamp (necessary to satisfy write order invariant).
   * Therefore a checkpoint could occur during the middle, after some pages have been assigned a
   * timestamp and before others. This means that a crash recovery could recover the tail of a
   * chain, but not its head. This does not cause corruption, but does cause permanent loss of the
   * pages that were recovered at the right end but never linked to a data page. Current remedy:
   * save/reload data. Such dangling chains can be detected by IntegrityCheck and a future remedy
   * would be for IntegrityCheck to move them back to the garbage chain.
   *
   * <p>If this method is called in the context of a transaction, it writes each page immediately to
   * the journal. This allows recovery to rebuild the long record for a recovered transaction that
   * committed after the keystone checkpoint.
   *
   * @param value The value. Must be in "long record mode"
   * @param inTxn indicates whether this operation is within the context of a transaction.
   * @throws PersistitException
   */
  long storeLongRecord(final Value value, final boolean inTxn) throws PersistitException {
    value.changeLongRecordMode(true);

    // Calculate how many LONG_RECORD pages we will need.
    //
    boolean completed = false;
    final int longSize = value.getLongSize();
    final byte[] longBytes = value.getLongBytes();
    final byte[] rawBytes = value.getEncodedBytes();
    final int maxSegmentSize = _volume.getPool().getBufferSize() - HEADER_SIZE;

    Debug.$assert0.t(value.isLongRecordMode());
    Debug.$assert0.t(rawBytes.length == LONGREC_SIZE);

    System.arraycopy(longBytes, 0, rawBytes, LONGREC_PREFIX_OFFSET, LONGREC_PREFIX_SIZE);

    long looseChain = 0;

    sequence(LONG_RECORD_ALLOCATE_A);

    Buffer buffer = null;
    int offset =
        LONGREC_PREFIX_SIZE
            + (((longSize - LONGREC_PREFIX_SIZE - 1) / maxSegmentSize) * maxSegmentSize);
    try {
      for (; ; ) {
        while (offset >= LONGREC_PREFIX_SIZE) {
          buffer = _volume.getStructure().allocPage();
          final long timestamp = _persistit.getTimestampAllocator().updateTimestamp();
          buffer.writePageOnCheckpoint(timestamp);
          buffer.init(PAGE_TYPE_LONG_RECORD);

          int segmentSize = longSize - offset;
          if (segmentSize > maxSegmentSize) segmentSize = maxSegmentSize;

          Debug.$assert0.t(
              segmentSize >= 0
                  && offset >= 0
                  && offset + segmentSize <= longBytes.length
                  && HEADER_SIZE + segmentSize <= buffer.getBytes().length);

          System.arraycopy(longBytes, offset, buffer.getBytes(), HEADER_SIZE, segmentSize);

          final int end = HEADER_SIZE + segmentSize;
          if (end < buffer.getBufferSize()) {
            buffer.clearBytes(end, buffer.getBufferSize());
          }
          buffer.setRightSibling(looseChain);
          looseChain = buffer.getPageAddress();
          buffer.setDirtyAtTimestamp(timestamp);
          if (inTxn) {
            buffer.writePage();
          }
          buffer.releaseTouched();
          offset -= maxSegmentSize;
          buffer = null;
        }

        final long page = looseChain;
        looseChain = 0;
        Buffer.writeLongRecordDescriptor(value.getEncodedBytes(), longSize, page);
        completed = true;

        return page;
      }
    } finally {
      if (buffer != null) buffer.releaseTouched();
      if (looseChain != 0) {
        _volume.getStructure().deallocateGarbageChain(looseChain, 0);
      }
      if (!completed) {
        value.changeLongRecordMode(false);
      }
    }
  }