@Override
  public boolean hasRecord(int recordId,
                           @Nullable IntRef redirectToIdRef) throws IOException {
    if (recordId == NULL_ID) {
      return false;
    }
    checkRecordIdValid(recordId);
    if (!isRecordIdAllocated(recordId)) {
      return false;
    }
    int currentRecordId = recordId;
    for (int i = 0; i < MAX_REDIRECTS; i++) {
      long recordOffset = idToOffset(currentRecordId);
      Page page = storage.pageByOffset(recordOffset);
      int offsetOnPage = storage.toOffsetInPage(recordOffset);
      ByteBuffer buffer = page.rawPageBuffer();
      RecordLayout recordLayout = RecordLayout.recordLayout(buffer, offsetOnPage);
      byte recordType = recordLayout.recordType();

      if (redirectToIdRef != null) {
        redirectToIdRef.set(currentRecordId);
      }

      if (recordType == RecordLayout.RECORD_TYPE_ACTUAL) {
        return true;
      }

      if (recordType == RecordLayout.RECORD_TYPE_MOVED) {
        int redirectToId = recordLayout.redirectToId(buffer, offsetOnPage);
        if (redirectToId == NULL_ID) {
          return false;
        }
        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 boolean hasRecord(int recordId,
                           @Nullable IntRef redirectToIdRef)