@Override
  public <Out> Out readRecord(int recordId,
                              @NotNull ByteBufferReader<Out> reader,
                              @Nullable IntRef redirectToIdRef) throws IOException {
    checkRecordIdExists(recordId);
    int currentRecordId = recordId;
    for (int i = 0; i < MAX_REDIRECTS; i++) {
      long recordOffsetInFile = idToOffset(currentRecordId);
      int offsetOnPage = storage.toOffsetInPage(recordOffsetInFile);
      Page page = storage.pageByOffset(recordOffsetInFile);
      ByteBuffer buffer = page.rawPageBuffer();
      RecordLayout recordLayout = RecordLayout.recordLayout(buffer, offsetOnPage);
      byte recordType = recordLayout.recordType();

      if (redirectToIdRef != null) {
        redirectToIdRef.set(currentRecordId); //will be overwritten if we follow .redirectedToId chain
      }

      if (recordType == RecordLayout.RECORD_TYPE_ACTUAL) {
        int recordPayloadLength = recordLayout.length(buffer, offsetOnPage);
        ByteBuffer slice = buffer.slice(offsetOnPage + recordLayout.headerSize(), recordPayloadLength)
          .asReadOnlyBuffer()
          .order(buffer.order());
        return reader.read(slice);
      }

      if (recordType == RecordLayout.RECORD_TYPE_MOVED) {
        int redirectToId = recordLayout.redirectToId(buffer, offsetOnPage);
        checkRedirectToId(recordId, currentRecordId, redirectToId);
        currentRecordId = redirectToId;
      }
      else {
        throw new AssertionError("RecordType(" + recordType + ") should not appear in the chain: " +
                                 "it is either not implemented yet, or all wrong");
      }
    }
    throw new IOException("record[" + recordId + "].redirectTo chain is too long (>=" + MAX_REDIRECTS + "): circular reference?");
  }
@Override
  public <Out> Out readRecord(int recordId,
                              @NotNull ByteBufferReader<Out> reader,
                              @Nullable IntRef redirectToIdRef)